Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
fake "nautilus" binary to make SmartGit able to open folders on non-nautilus-powered desktop envs (like KDE or XFCE or MATE or Budgie)
// 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