Created
September 27, 2020 13:49
-
-
Save divinity76/73ee57eadf52d46b699499c5a8c24c34 to your computer and use it in GitHub Desktop.
fake "nautilus" binary to make SmartGit able to open folders on non-nautilus-powered desktop envs (like KDE or XFCE or MATE or Budgie)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// compile invocation: | |
// prod: g++ -static -s -Os -Wall -Wextra -Wpedantic -Werror -o fake_nautilus fake_nautilus.cpp | |
// dev: g++ -D__DEV__ -O0 -Wall -Wextra -Wpedantic -Werror -o fake_nautilus fake_nautilus.cpp | |
#include <iostream> | |
#include <string> | |
#include <fstream> | |
#include <algorithm> | |
// https://github.com/divinity76/phpcpp | |
namespace php | |
{ | |
// https://www.php.net/manual/en/function.escapeshellarg.php | |
std::string escapeshellarg(const std::string &$arg) | |
{ | |
std::string ret; | |
ret.reserve($arg.length() + 100); // should be enough to avoid realloc() in most cases. | |
ret += "'"; | |
for (size_t i = 0; i < $arg.length(); ++i) | |
{ | |
if ($arg[i] == '\00') | |
{ | |
throw std::runtime_error("argument contains null bytes, it's literally impossible to escape null bytes in bash!"); | |
} | |
else if ($arg[i] == '\'') | |
{ | |
ret += "'\\''"; | |
} | |
else | |
{ | |
ret += $arg[i]; | |
} | |
} | |
ret += "'"; | |
return ret; | |
} | |
// on Windows one should use std::filesystem::operator/ which would make it compatible with Windows | |
// https://bugs.php.net/bug.php?id=80155 | |
// https://www.php.net/manual/en/function.basename | |
std::string basename(std::string $path, const std::string &$suffix = "") | |
{ | |
const size_t last_pos = $path.find_last_of('/'); | |
if (last_pos != std::string::npos) | |
{ | |
$path = $path.substr(last_pos + sizeof('/')); | |
} | |
if ($suffix.size() > 0) | |
{ | |
const size_t suffix_pos = $path.rfind($suffix); | |
if (suffix_pos != std::string::npos) | |
{ | |
if ((suffix_pos + $suffix.size()) == $path.size()) | |
{ | |
$path.resize(suffix_pos); | |
} | |
} | |
} | |
return $path; | |
} | |
// https://www.php.net/manual/en/function.rtrim.php | |
std::string rtrim(std::string $str, const std::string &$character_mask = std::string("\x20\x09\x0A\x0D\x00\x0B", 6)) | |
{ | |
// optimizeme: can this be optimized to a single erase() call? probably. | |
while ($str.size() > 0 && $character_mask.find_first_of($str.back()) != std::string::npos) | |
{ | |
$str.pop_back(); | |
} | |
return $str; | |
} | |
// https://www.php.net/manual/en/function.ltrim.php | |
std::string ltrim(std::string $str, const std::string &$character_mask = std::string("\x20\x09\x0A\x0D\x00\x0B", 6)) | |
{ | |
// optimizeme: can this be optimized to a single erase() call? probably. | |
while ($str.size() > 0 && $character_mask.find_first_of($str.front()) != std::string::npos) | |
{ | |
$str.erase(0, 1); | |
} | |
return $str; | |
} | |
// https://www.php.net/manual/en/function.rtrim.php | |
std::string trim(std::string $str, const std::string &$character_mask = std::string("\x20\x09\x0A\x0D\x00\x0B", 6)) | |
{ | |
return rtrim(ltrim($str, $character_mask), $character_mask); | |
} | |
// https://www.php.net/manual/en/function.strtolower.php | |
std::string strtolower(std::string $string) | |
{ | |
std::transform($string.begin(), $string.end(), $string.begin(), ::tolower); | |
return $string; | |
} | |
} // namespace php | |
void log( | |
const std::string &str_to_log, | |
const std::string &logfile = "/tmp/fake_nautilus_logfile.log", | |
const std::string &log_prepend = "----------\n", | |
const std::string &log_append = "\n----------\n") | |
{ | |
std::ofstream file(logfile, std::ofstream::binary | std::ofstream::app | std::ofstream::out | std::ofstream::ate); | |
file.exceptions(std::ofstream::failbit | std::ofstream::badbit); | |
file << log_prepend << str_to_log << log_append; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
#ifdef __DEV__ | |
for (int i = 0; i < argc; ++i) | |
{ | |
log("argv[" + std::to_string(i) + "]: " + argv[i]); | |
} | |
#endif | |
if (argc != 2) | |
{ | |
std::cerr << "error: invalid number of arguments\n" | |
"usage: " | |
<< php::escapeshellarg(argv[0]) << " " << php::escapeshellarg("file:///absolute/path/to/file") << std::endl; | |
exit(EXIT_FAILURE); | |
} | |
const std::string in_raw(argv[1]); | |
std::string in(in_raw); | |
if (in_raw.find("file:///") != 0) | |
{ | |
std::cerr << "error: filepath argument did not start with file:/// \n" | |
<< "(PS, unlike the real nautilus, i don't support relative paths, you MUST supply absolute paths, like SmartGit does)\n" | |
<< "usage: " << php::escapeshellarg(argv[0]) << " " << php::escapeshellarg("file:///absolute/path/to/file") << std::endl; | |
exit(EXIT_FAILURE); | |
} | |
in = in.substr(sizeof("file://") - sizeof("")); // - null terminator | |
const std::string target_file = php::basename(in); | |
const std::string target_dir = in.substr(0, in.size() - target_file.size()); | |
const std::string xdg_current_desktop = php::strtolower(php::trim(getenv("XDG_CURRENT_DESKTOP"))); | |
const auto xdg_match = [xdg_current_desktop](const std::string &search) -> bool { | |
return (xdg_current_desktop.find(php::trim(php::strtolower(search))) != std::string::npos); | |
}; | |
#if __DEV__ | |
log("in_raw: " + in_raw); | |
log("in: " + in); | |
log("target_file: " + target_file); | |
log("target_dir: " + target_dir); | |
log("xdg_current_desktop: " + xdg_current_desktop); | |
#endif | |
if (xdg_match("XFCE")) | |
{ | |
// XFCE desktop | |
// tested on Xubuntu 18.04 | |
// tested on Xubuntu 20.04 | |
// this should work on XFCE since 2006, here is the commit adding support: https://github.com/xfce-mirror/thunar/commit/6342c7d582421a7c473ff5289a650e2894a316cd | |
std::string cmd = "dbus-send "; | |
cmd += "--type=method_call "; | |
cmd += "--dest=org.xfce.Thunar "; | |
cmd += "/org/xfce/FileManager "; | |
cmd += "org.xfce.FileManager.DisplayFolderAndSelect "; | |
cmd += "string:" + php::escapeshellarg(target_dir) + " "; | |
std::string tmp = target_file; | |
if(target_file.length() < 1){ | |
// important for XFCE: when you want to select a folder rather than a file in a folder, | |
// the target must apparently be . | |
tmp = "."; | |
} | |
cmd += "string:" + php::escapeshellarg(tmp) + " "; | |
cmd += "string:" + php::escapeshellarg("") + " "; // ??? | |
cmd += "string:" + php::escapeshellarg(""); // ??? | |
#ifdef __DEV__ | |
// should now look like: | |
// dbus-send --type=method_call --dest=org.xfce.Thunar /org/xfce/FileManager org.xfce.FileManager.DisplayFolderAndSelect string:"/path/to/parent/folder" string:"Filename_to_focus" string:"" string:"" | |
log("cmd: " + cmd); | |
#endif | |
const int ret = system(cmd.c_str()); | |
exit(ret); | |
} | |
else if ( | |
xdg_match("Budgie") // Budgie desktop, tested on Ubuntu Budgie 20.04 | |
|| xdg_match("GNOME") // GNOME desktop, tested on Ubuntu 20.04 | |
|| xdg_match("MATE") // MATE desktop, tested on Ubuntu Mate 20.04 | |
|| xdg_match("KDE") // KDE desktop, tested on Kubuntu 20.04 | |
) | |
{ | |
// specifications: https://www.freedesktop.org/wiki/Specifications/file-manager-interface/ | |
std::string cmd = "dbus-send "; | |
cmd += "--type=method_call "; | |
cmd += "--dest=org.freedesktop.FileManager1 "; | |
cmd += "/org/freedesktop/FileManager1 "; | |
cmd += "org.freedesktop.FileManager1.ShowItems "; | |
std::string tmp = "file://" + target_dir + "/" + target_file; | |
cmd += "array:string:" + php::escapeshellarg(tmp) + " "; | |
cmd += "string:" + php::escapeshellarg(""); // ??? | |
#ifdef __DEV__ | |
// now it should look something like: | |
// dbus-send --type=method_call --dest=org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1.ShowItems array:string:"file://$1/$2" string:"" | |
log("cmd: " + cmd); | |
#endif | |
const int ret = system(cmd.c_str()); | |
exit(ret); | |
} | |
else if (xdg_match("LXQt")) | |
{ | |
std::cerr << "Sorry, LXQt is not supported (i have no idea how to make this work on lxqt)" << std::endl; | |
exit(EXIT_FAILURE); | |
} | |
else | |
{ | |
// unknown desktop enviorment.. | |
// using a fallback that "seems to work most places" | |
// | |
// specifications: https://www.freedesktop.org/wiki/Specifications/file-manager-interface/ | |
std::string cmd = "dbus-send "; | |
cmd += "--type=method_call "; | |
cmd += "--dest=org.freedesktop.FileManager1 "; | |
cmd += "/org/freedesktop/FileManager1 "; | |
cmd += "org.freedesktop.FileManager1.ShowItems "; | |
std::string tmp = "file://" + target_dir + "/" + target_file; | |
cmd += "array:string:" + php::escapeshellarg(tmp) + " "; | |
cmd += "string:" + php::escapeshellarg(""); // ??? | |
#ifdef __DEV__ | |
// now it should look something like: | |
// dbus-send --type=method_call --dest=org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1.ShowItems array:string:"file://$1/$2" string:"" | |
log("cmd: " + cmd); | |
#endif | |
const int ret = system(cmd.c_str()); | |
exit(ret); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment