Use semicolon as file path separator for lua_load (#2154)
* Use semicolon as file path separator for lua_load. * Resolve lua path in lua_load. * Move llua_init to initialisation(), to ensure lua state is initialized before dealing with config file. * Move package.path setup to llua_init so globals all get set from the same place. * Handle paths with spaces correctly in `to_real_path` by escaping spaces. Signed-off-by: Tin Švagelj <tin.svagelj@live.com>
This commit is contained in:
@@ -266,7 +266,18 @@ values:
|
|||||||
- function_name
|
- function_name
|
||||||
- [function arguments]
|
- [function arguments]
|
||||||
- name: lua_load
|
- name: lua_load
|
||||||
desc: Loads the Lua scripts separated by spaces.
|
desc: |-
|
||||||
|
List of Lua script paths to load at startup in order to provide Lua
|
||||||
|
functions for other hooks. Listed files are loaded (executed) before
|
||||||
|
'lua_startup_hook' and can (but shouldn't) run code in global scope.
|
||||||
|
|
||||||
|
Paths are ';' (semicolon) separated, and can be relative to the config
|
||||||
|
file path, or absolute.
|
||||||
|
|
||||||
|
The paths were previously ' ' (space) separated, this functionality is
|
||||||
|
still supported if ';' isn't found, but is deprecated and will be removed
|
||||||
|
in future versions. Empty paths are skipped so './example file.lua;' is
|
||||||
|
valid.
|
||||||
- name: lua_mouse_hook
|
- name: lua_mouse_hook
|
||||||
desc: |-
|
desc: |-
|
||||||
This function, if defined, will be called by Conky upon receiving mouse
|
This function, if defined, will be called by Conky upon receiving mouse
|
||||||
|
@@ -146,10 +146,14 @@ std::filesystem::path to_real_path(const std::string &source) {
|
|||||||
wordexp_t p;
|
wordexp_t p;
|
||||||
char **w;
|
char **w;
|
||||||
int i;
|
int i;
|
||||||
const char *csource = source.c_str();
|
std::string checked = std::string(source);
|
||||||
if (wordexp(csource, &p, 0) != 0) {
|
std::string::size_type n = 0;
|
||||||
return std::string();
|
while ((n = checked.find(" ", n)) != std::string::npos) {
|
||||||
|
checked.replace(n, 1, "\\ ");
|
||||||
|
n += 2;
|
||||||
}
|
}
|
||||||
|
const char *csource = source.c_str();
|
||||||
|
if (wordexp(checked.c_str(), &p, 0) != 0) { return std::string(); }
|
||||||
w = p.we_wordv;
|
w = p.we_wordv;
|
||||||
const char *resolved_path = strdup(w[0]);
|
const char *resolved_path = strdup(w[0]);
|
||||||
wordfree(&p);
|
wordfree(&p);
|
||||||
|
37
src/conky.cc
37
src/conky.cc
@@ -121,7 +121,6 @@
|
|||||||
#include "data/timeinfo.h"
|
#include "data/timeinfo.h"
|
||||||
#include "data/top.h"
|
#include "data/top.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "lua/llua.h"
|
|
||||||
#include "output/nc.h"
|
#include "output/nc.h"
|
||||||
|
|
||||||
#ifdef BUILD_MYSQL
|
#ifdef BUILD_MYSQL
|
||||||
@@ -134,6 +133,7 @@
|
|||||||
#include "data/network/ccurl_thread.h"
|
#include "data/network/ccurl_thread.h"
|
||||||
#endif /* BUILD_CURL */
|
#endif /* BUILD_CURL */
|
||||||
|
|
||||||
|
#include "lua/llua.h"
|
||||||
#include "lua/lua-config.hh"
|
#include "lua/lua-config.hh"
|
||||||
#include "lua/setting.hh"
|
#include "lua/setting.hh"
|
||||||
#include "output/display-output.hh"
|
#include "output/display-output.hh"
|
||||||
@@ -1985,40 +1985,6 @@ void load_config_file() {
|
|||||||
lua::stack_sentry s(l);
|
lua::stack_sentry s(l);
|
||||||
l.checkstack(2);
|
l.checkstack(2);
|
||||||
|
|
||||||
// Extend lua package.path so scripts can use relative paths
|
|
||||||
{
|
|
||||||
struct stat file_stat {};
|
|
||||||
|
|
||||||
std::string path_ext;
|
|
||||||
|
|
||||||
// add XDG directory to lua path
|
|
||||||
auto xdg_path =
|
|
||||||
std::filesystem::path(to_real_path(XDG_CONFIG_FILE)).parent_path();
|
|
||||||
if (stat(xdg_path.c_str(), &file_stat) == 0) {
|
|
||||||
path_ext.push_back(';');
|
|
||||||
path_ext.append(xdg_path);
|
|
||||||
path_ext.append("/?.lua");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto parent_path = current_config.parent_path();
|
|
||||||
if (xdg_path != parent_path && stat(path_ext.c_str(), &file_stat) == 0) {
|
|
||||||
path_ext.push_back(';');
|
|
||||||
path_ext.append(parent_path);
|
|
||||||
path_ext.append("/?.lua");
|
|
||||||
}
|
|
||||||
|
|
||||||
l.getglobal("package");
|
|
||||||
l.getfield(-1, "path");
|
|
||||||
|
|
||||||
auto path = l.tostring(-1);
|
|
||||||
path.append(path_ext);
|
|
||||||
l.pop();
|
|
||||||
l.pushstring(path.c_str());
|
|
||||||
|
|
||||||
l.setfield(-2, "path");
|
|
||||||
l.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
#ifdef BUILD_BUILTIN_CONFIG
|
#ifdef BUILD_BUILTIN_CONFIG
|
||||||
if (current_config == builtin_config_magic) {
|
if (current_config == builtin_config_magic) {
|
||||||
@@ -2171,6 +2137,7 @@ void initialisation(int argc, char **argv) {
|
|||||||
set_default_configurations();
|
set_default_configurations();
|
||||||
|
|
||||||
set_current_config();
|
set_current_config();
|
||||||
|
llua_init();
|
||||||
load_config_file();
|
load_config_file();
|
||||||
|
|
||||||
/* handle other command line arguments */
|
/* handle other command line arguments */
|
||||||
|
148
src/lua/llua.cc
148
src/lua/llua.cc
@@ -22,18 +22,22 @@
|
|||||||
*/
|
*/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "build.h"
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "../conky.h"
|
#include "../conky.h"
|
||||||
#include "../geometry.h"
|
#include "../geometry.h"
|
||||||
#include "llua.h"
|
|
||||||
#include "../logging.h"
|
#include "../logging.h"
|
||||||
|
#include "build.h"
|
||||||
|
#include "llua.h"
|
||||||
|
|
||||||
#ifdef BUILD_GUI
|
#ifdef BUILD_GUI
|
||||||
#include "../output/gui.h"
|
#include "../output/gui.h"
|
||||||
|
|
||||||
#ifdef BUILD_X11
|
#ifdef BUILD_X11
|
||||||
#include "x11-settings.h"
|
|
||||||
#include "../output/x11.h"
|
#include "../output/x11.h"
|
||||||
|
#include "x11-settings.h"
|
||||||
#endif /* BUILD_X11 */
|
#endif /* BUILD_X11 */
|
||||||
|
|
||||||
#ifdef BUILD_MOUSE_EVENTS
|
#ifdef BUILD_MOUSE_EVENTS
|
||||||
@@ -75,13 +79,26 @@ class lua_load_setting : public conky::simple_config_setting<std::string> {
|
|||||||
|
|
||||||
if (init) {
|
if (init) {
|
||||||
std::string files = do_convert(l, -1).first;
|
std::string files = do_convert(l, -1).first;
|
||||||
while (!files.empty()) {
|
|
||||||
std::string::size_type pos = files.find(' ');
|
// Split file names into separate `\0` strings
|
||||||
if (pos > 0) {
|
if (files.find(';') != std::string::npos) {
|
||||||
std::string file(files, 0, pos);
|
for (auto &ch : files) {
|
||||||
llua_load(file.c_str());
|
if (ch == ';') { ch = '\0'; }
|
||||||
}
|
}
|
||||||
files.erase(0, pos == std::string::npos ? pos : pos + 1);
|
} else {
|
||||||
|
// TODO: Remove space-delimited file name handling in 3 years (2028.)
|
||||||
|
for (auto &ch : files) {
|
||||||
|
if (ch == ' ') { ch = '\0'; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *start = files.c_str();
|
||||||
|
const char *end = start + files.size();
|
||||||
|
while (start < end) {
|
||||||
|
if (*start != '\0') { // Skip empty strings
|
||||||
|
llua_load(start);
|
||||||
|
}
|
||||||
|
start += strlen(start) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +111,6 @@ class lua_load_setting : public conky::simple_config_setting<std::string> {
|
|||||||
#ifdef HAVE_SYS_INOTIFY_H
|
#ifdef HAVE_SYS_INOTIFY_H
|
||||||
llua_rm_notifies();
|
llua_rm_notifies();
|
||||||
#endif /* HAVE_SYS_INOTIFY_H */
|
#endif /* HAVE_SYS_INOTIFY_H */
|
||||||
if (lua_L == nullptr) { return; }
|
|
||||||
lua_close(lua_L);
|
lua_close(lua_L);
|
||||||
lua_L = nullptr;
|
lua_L = nullptr;
|
||||||
}
|
}
|
||||||
@@ -171,15 +187,50 @@ void llua_init() {
|
|||||||
|
|
||||||
/* add our library path to the lua package.cpath global var */
|
/* add our library path to the lua package.cpath global var */
|
||||||
luaL_openlibs(lua_L);
|
luaL_openlibs(lua_L);
|
||||||
lua_getglobal(lua_L, "package");
|
lua_getglobal(lua_L, "package"); // stack: package
|
||||||
lua_getfield(lua_L, -1, "cpath");
|
lua_getfield(lua_L, -1, "cpath"); // stack: package.cpath, package
|
||||||
|
|
||||||
old_path = std::string(lua_tostring(lua_L, -1));
|
old_path = std::string(lua_tostring(lua_L, -1));
|
||||||
new_path = libs + old_path;
|
new_path = libs + old_path;
|
||||||
|
|
||||||
lua_pushstring(lua_L, new_path.c_str());
|
lua_pushstring(lua_L,
|
||||||
lua_setfield(lua_L, -3, "cpath");
|
new_path.c_str()); // stack: new_path, package.cpath, package
|
||||||
lua_pop(lua_L, 2);
|
lua_setfield(lua_L, -3, "cpath"); // stack: package.cpath, package
|
||||||
|
lua_pop(lua_L, 1); // stack: package
|
||||||
|
|
||||||
|
/* Add config file and XDG paths to package.path so scripts can load other
|
||||||
|
* scripts from relative paths */
|
||||||
|
{
|
||||||
|
struct stat file_stat{};
|
||||||
|
|
||||||
|
std::string path_ext;
|
||||||
|
|
||||||
|
// add XDG directory to lua path
|
||||||
|
auto xdg_path =
|
||||||
|
std::filesystem::path(to_real_path(XDG_CONFIG_FILE)).parent_path();
|
||||||
|
if (stat(xdg_path.c_str(), &file_stat) == 0) {
|
||||||
|
path_ext.append(xdg_path);
|
||||||
|
path_ext.append("/?.lua");
|
||||||
|
path_ext.push_back(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
auto parent_path = current_config.parent_path();
|
||||||
|
if (xdg_path != parent_path && stat(path_ext.c_str(), &file_stat) == 0) {
|
||||||
|
path_ext.append(parent_path);
|
||||||
|
path_ext.append("/?.lua");
|
||||||
|
path_ext.push_back(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(lua_L, -1, "path"); // stack: package.path, package
|
||||||
|
old_path = std::string(lua_tostring(lua_L, -1));
|
||||||
|
new_path = path_ext + old_path;
|
||||||
|
|
||||||
|
lua_pushstring(lua_L,
|
||||||
|
new_path.c_str()); // stack: new_path, package.path, package
|
||||||
|
lua_setfield(lua_L, -3, "path"); // stack: package.path, package
|
||||||
|
lua_pop(lua_L, 1); // stack: package
|
||||||
|
}
|
||||||
|
lua_pop(lua_L, 1); // stack is empty
|
||||||
|
|
||||||
lua_pushstring(lua_L, PACKAGE_NAME " " VERSION " compiled for " BUILD_ARCH);
|
lua_pushstring(lua_L, PACKAGE_NAME " " VERSION " compiled for " BUILD_ARCH);
|
||||||
lua_setglobal(lua_L, "conky_build_info");
|
lua_setglobal(lua_L, "conky_build_info");
|
||||||
@@ -216,17 +267,46 @@ inline bool file_exists(const char *path) {
|
|||||||
void llua_load(const char *script) {
|
void llua_load(const char *script) {
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
std::string path = to_real_path(script);
|
std::filesystem::path path = to_real_path(script);
|
||||||
|
|
||||||
if (!file_exists(path.c_str())) {
|
if (!file_exists(path.c_str())) {
|
||||||
NORM_ERR("llua_load: specified script file '%s' doesn't exist",
|
bool found_alternative = false;
|
||||||
path.c_str());
|
|
||||||
// return without initializing lua_L because other parts of the code rely
|
|
||||||
// on it being null if the script is not loaded
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
llua_init();
|
// Try resolving file name by using files in lua path:
|
||||||
|
lua_getglobal(lua_L, "package"); // stack: package
|
||||||
|
lua_getfield(lua_L, -1, "path"); // stack: package.path, package
|
||||||
|
auto lua_path = lua_tostring(lua_L, -1);
|
||||||
|
lua_pop(lua_L, 2); // stack is empty
|
||||||
|
|
||||||
|
std::stringstream path_stream(lua_path);
|
||||||
|
std::string current;
|
||||||
|
while (std::getline(path_stream, current, ';')) {
|
||||||
|
// lua_load conky variable accepts full file names, so replace "?.lua"
|
||||||
|
// with "?" to ensure file names don't get the unexpected .lua suffix; but
|
||||||
|
// modules with init.lua will still work.
|
||||||
|
size_t substitute_pos = current.find("?.lua");
|
||||||
|
if (substitute_pos != std::string::npos) {
|
||||||
|
current.replace(substitute_pos, 5, "?");
|
||||||
|
}
|
||||||
|
|
||||||
|
substitute_pos = current.find('?');
|
||||||
|
if (substitute_pos == std::string::npos) { continue; }
|
||||||
|
current.replace(substitute_pos, 1, script);
|
||||||
|
path = to_real_path(current);
|
||||||
|
|
||||||
|
if (file_exists(path.c_str())) {
|
||||||
|
found_alternative = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_alternative) {
|
||||||
|
NORM_ERR("llua_load: specified script file '%s' doesn't exist", script);
|
||||||
|
// return without initializing lua_L because other parts of the code rely
|
||||||
|
// on it being null if the script is not loaded
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
error = luaL_dofile(lua_L, path.c_str());
|
error = luaL_dofile(lua_L, path.c_str());
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
@@ -347,8 +427,6 @@ static char *llua_getstring(const char *args) {
|
|||||||
char *func;
|
char *func;
|
||||||
char *ret = nullptr;
|
char *ret = nullptr;
|
||||||
|
|
||||||
if (lua_L == nullptr) { return nullptr; }
|
|
||||||
|
|
||||||
func = llua_do_call(args, 1);
|
func = llua_do_call(args, 1);
|
||||||
if (func != nullptr) {
|
if (func != nullptr) {
|
||||||
if (lua_isstring(lua_L, -1) == 0) {
|
if (lua_isstring(lua_L, -1) == 0) {
|
||||||
@@ -392,8 +470,6 @@ static char *llua_getstring_read(const char *function, const char *arg)
|
|||||||
static int llua_getnumber(const char *args, double *ret) {
|
static int llua_getnumber(const char *args, double *ret) {
|
||||||
char *func;
|
char *func;
|
||||||
|
|
||||||
if (lua_L == nullptr) { return 0; }
|
|
||||||
|
|
||||||
func = llua_do_call(args, 1);
|
func = llua_do_call(args, 1);
|
||||||
if (func != nullptr) {
|
if (func != nullptr) {
|
||||||
if (lua_isnumber(lua_L, -1) == 0) {
|
if (lua_isnumber(lua_L, -1) == 0) {
|
||||||
@@ -489,32 +565,30 @@ void llua_set_number(const char *key, double value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void llua_startup_hook() {
|
void llua_startup_hook() {
|
||||||
if ((lua_L == nullptr) || lua_startup_hook.get(*state).empty()) { return; }
|
if (lua_startup_hook.get(*state).empty()) { return; }
|
||||||
llua_do_call(lua_startup_hook.get(*state).c_str(), 0);
|
llua_do_call(lua_startup_hook.get(*state).c_str(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void llua_shutdown_hook() {
|
void llua_shutdown_hook() {
|
||||||
if ((lua_L == nullptr) || lua_shutdown_hook.get(*state).empty()) { return; }
|
if (lua_shutdown_hook.get(*state).empty()) { return; }
|
||||||
llua_do_call(lua_shutdown_hook.get(*state).c_str(), 0);
|
llua_do_call(lua_shutdown_hook.get(*state).c_str(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILD_GUI
|
#ifdef BUILD_GUI
|
||||||
void llua_draw_pre_hook() {
|
void llua_draw_pre_hook() {
|
||||||
if ((lua_L == nullptr) || lua_draw_hook_pre.get(*state).empty()) { return; }
|
if (lua_draw_hook_pre.get(*state).empty()) { return; }
|
||||||
llua_do_call(lua_draw_hook_pre.get(*state).c_str(), 0);
|
llua_do_call(lua_draw_hook_pre.get(*state).c_str(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void llua_draw_post_hook() {
|
void llua_draw_post_hook() {
|
||||||
if ((lua_L == nullptr) || lua_draw_hook_post.get(*state).empty()) { return; }
|
if (lua_draw_hook_post.get(*state).empty()) { return; }
|
||||||
llua_do_call(lua_draw_hook_post.get(*state).c_str(), 0);
|
llua_do_call(lua_draw_hook_post.get(*state).c_str(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILD_MOUSE_EVENTS
|
#ifdef BUILD_MOUSE_EVENTS
|
||||||
template <typename EventT>
|
template <typename EventT>
|
||||||
bool llua_mouse_hook(const EventT &ev) {
|
bool llua_mouse_hook(const EventT &ev) {
|
||||||
if ((lua_L == nullptr) || lua_mouse_hook.get(*state).empty()) {
|
if (lua_mouse_hook.get(*state).empty()) { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const std::string raw_hook_name = lua_mouse_hook.get(*state);
|
const std::string raw_hook_name = lua_mouse_hook.get(*state);
|
||||||
std::string hook_name;
|
std::string hook_name;
|
||||||
if (raw_hook_name.rfind("conky_", 0) == 0) {
|
if (raw_hook_name.rfind("conky_", 0) == 0) {
|
||||||
@@ -579,7 +653,6 @@ void llua_set_userdata(const char *key, const char *type, void *value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void llua_setup_window_table(conky::rect<int> text_rect) {
|
void llua_setup_window_table(conky::rect<int> text_rect) {
|
||||||
if (lua_L == nullptr) { return; }
|
|
||||||
lua_newtable(lua_L);
|
lua_newtable(lua_L);
|
||||||
|
|
||||||
#ifdef BUILD_X11
|
#ifdef BUILD_X11
|
||||||
@@ -611,8 +684,6 @@ void llua_setup_window_table(conky::rect<int> text_rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void llua_update_window_table(conky::rect<int> text_rect) {
|
void llua_update_window_table(conky::rect<int> text_rect) {
|
||||||
if (lua_L == nullptr) { return; }
|
|
||||||
|
|
||||||
lua_getglobal(lua_L, "conky_window");
|
lua_getglobal(lua_L, "conky_window");
|
||||||
if (lua_isnil(lua_L, -1)) {
|
if (lua_isnil(lua_L, -1)) {
|
||||||
/* window table isn't populated yet */
|
/* window table isn't populated yet */
|
||||||
@@ -635,7 +706,6 @@ void llua_update_window_table(conky::rect<int> text_rect) {
|
|||||||
#endif /* BUILD_GUI */
|
#endif /* BUILD_GUI */
|
||||||
|
|
||||||
void llua_setup_info(struct information *i, double u_interval) {
|
void llua_setup_info(struct information *i, double u_interval) {
|
||||||
if (lua_L == nullptr) { return; }
|
|
||||||
lua_newtable(lua_L);
|
lua_newtable(lua_L);
|
||||||
|
|
||||||
llua_set_number("update_interval", u_interval);
|
llua_set_number("update_interval", u_interval);
|
||||||
@@ -645,8 +715,6 @@ void llua_setup_info(struct information *i, double u_interval) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void llua_update_info(struct information *i, double u_interval) {
|
void llua_update_info(struct information *i, double u_interval) {
|
||||||
if (lua_L == nullptr) { return; }
|
|
||||||
|
|
||||||
lua_getglobal(lua_L, "conky_info");
|
lua_getglobal(lua_L, "conky_info");
|
||||||
if (lua_isnil(lua_L, -1)) {
|
if (lua_isnil(lua_L, -1)) {
|
||||||
/* window table isn't populated yet */
|
/* window table isn't populated yet */
|
||||||
|
@@ -44,6 +44,7 @@ extern "C" {
|
|||||||
void llua_inotify_query(int wd, int mask);
|
void llua_inotify_query(int wd, int mask);
|
||||||
#endif /* HAVE_SYS_INOTIFY_H */
|
#endif /* HAVE_SYS_INOTIFY_H */
|
||||||
|
|
||||||
|
void llua_init();
|
||||||
void llua_startup_hook(void);
|
void llua_startup_hook(void);
|
||||||
void llua_shutdown_hook(void);
|
void llua_shutdown_hook(void);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user