diff --git a/src/debug_lldb.cc b/src/debug_lldb.cc index c786eb8..20599e6 100644 --- a/src/debug_lldb.cc +++ b/src/debug_lldb.cc @@ -53,55 +53,85 @@ Debug::LLDB::LLDB(): state(lldb::StateType::eStateInvalid), buffer_size(131072) #endif } -void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path, - const std::vector > &breakpoints, - std::function callback, - std::function status_callback, - std::function stop_callback, - const std::string &remote_host) { - if(!debugger) { - lldb::SBDebugger::Initialize(); - debugger=std::make_unique(lldb::SBDebugger::Create(true, log, nullptr)); - listener=std::make_unique("juCi++ lldb listener"); - } - - //Create executable string and argument array +std::tuple, std::string, std::vector > Debug::LLDB::parse_run_arguments(const std::string &command) { + std::vector environment; std::string executable; std::vector arguments; + size_t start_pos=std::string::npos; bool quote=false; bool double_quote=false; - bool symbol=false; + size_t backslash_count=0; for(size_t c=0;c<=command.size();c++) { - if(c==command.size() || (!quote && !double_quote && !symbol && command[c]==' ')) { + if(c==command.size() || (!quote && !double_quote && backslash_count%2==0 && command[c]==' ')) { if(c>0 && start_pos!=std::string::npos) { + auto argument=command.substr(start_pos, c-start_pos); if(executable.empty()) { - executable=filesystem::unescape_argument(command.substr(start_pos, c-start_pos)); + //Check for environment variable + bool env_arg=false; + for(size_t c=0;c='a' && argument[c]<='z') || (argument[c]>='A' && argument[c]<='Z') || + (argument[c]>='0' && argument[c]<='9') || argument[c]=='_') + continue; + else if(argument[c]=='=' && c+1 > &breakpoints, + std::function callback, + std::function status_callback, + std::function stop_callback, + const std::string &remote_host) { + if(!debugger) { + lldb::SBDebugger::Initialize(); + debugger=std::make_unique(lldb::SBDebugger::Create(true, log, nullptr)); + listener=std::make_unique("juCi++ lldb listener"); + } + + //Create executable string and argument array + auto parsed_run_arguments=parse_run_arguments(command); + auto &environment_from_arguments=std::get<0>(parsed_run_arguments); + auto &executable=std::get<1>(parsed_run_arguments); + auto &arguments=std::get<2>(parsed_run_arguments); + + std::vector argv; for(size_t c=0;cCreateTarget(executable.c_str()); if(!target.IsValid()) { @@ -142,14 +172,31 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat } } } - const char *empty_parameter[1]; - empty_parameter[0]=nullptr; - process->RemoteLaunch(argv, empty_parameter, nullptr, nullptr, nullptr, nullptr, lldb::eLaunchFlagNone, false, error); + + // Create environment array + std::vector environment; + for(auto &e: environment_from_arguments) + environment.emplace_back(e.c_str()); + environment.emplace_back(nullptr); + + process->RemoteLaunch(argv.data(), environment.data(), nullptr, nullptr, nullptr, nullptr, lldb::eLaunchFlagNone, false, error); if(!error.Fail()) process->Continue(); } - else - process = std::make_unique(target.Launch(*listener, argv, const_cast(environ), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)); + else { + // Create environment array + std::vector environment; + for(auto &e: environment_from_arguments) + environment.emplace_back(e.c_str()); + size_t environ_size=0; + while(environ[environ_size]!=nullptr) + ++environ_size; + for(size_t c=0;c(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)); + } if(error.Fail()) { Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); if(callback) diff --git a/src/debug_lldb.h b/src/debug_lldb.h index 5c2adfc..8c0b28c 100644 --- a/src/debug_lldb.h +++ b/src/debug_lldb.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace Debug { class LLDB { @@ -72,6 +73,8 @@ namespace Debug { void write(const std::string &buffer); private: + std::tuple, std::string, std::vector> parse_run_arguments(const std::string &command); + std::unique_ptr debugger; std::unique_ptr listener; std::unique_ptr process; diff --git a/src/filesystem.cc b/src/filesystem.cc index 8b5f0b8..572ba59 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -155,21 +155,33 @@ std::string filesystem::unescape_argument(const std::string &argument) { (unescaped[0]=='"' && unescaped[unescaped.size()-1]=='"')) { char quotation_mark=unescaped[0]; unescaped=unescaped.substr(1, unescaped.size()-2); - for(size_t pos=1;pos='A' && *iter<='Z') || (*iter>='a' && *iter<='z') || *iter>=128)) return true; diff --git a/src/window.cc b/src/window.cc index 8d25f90..a8b74ad 100644 --- a/src/window.cc +++ b/src/window.cc @@ -1037,7 +1037,7 @@ void Window::set_menu_actions() { EntryBox::get().labels.emplace_back(); auto label_it=EntryBox::get().labels.begin(); label_it->update=[label_it](int state, const std::string& message){ - label_it->set_text("Set empty to let juCi++ deduce executable"); + label_it->set_text("Synopsis: [environment_variable=value]... executable [argument]...\nSet empty to let juCi++ deduce executable."); }; label_it->update(0, ""); EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ @@ -1134,7 +1134,7 @@ void Window::set_menu_actions() { EntryBox::get().labels.emplace_back(); auto label_it=EntryBox::get().labels.begin(); label_it->update=[label_it](int state, const std::string& message){ - label_it->set_text("Set empty to let juCi++ deduce executable"); + label_it->set_text("Synopsis: [environment_variable=value]... executable [argument]...\nSet empty to let juCi++ deduce executable."); }; label_it->update(0, ""); EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ diff --git a/tests/filesystem_test.cc b/tests/filesystem_test.cc index 959c0ef..ffd5cd1 100644 --- a/tests/filesystem_test.cc +++ b/tests/filesystem_test.cc @@ -22,6 +22,34 @@ int main() { auto unescaped=filesystem::unescape_argument("\"test \\'()\\\"\""); g_assert_cmpstr(unescaped.c_str(), ==, "test \\'()\""); } + { + auto unescaped=filesystem::unescape_argument("\\\\"); + g_assert_cmpstr(unescaped.c_str(), ==, "\\"); + } + { + auto unescaped=filesystem::unescape_argument("\\\\\\ "); + g_assert_cmpstr(unescaped.c_str(), ==, "\\ "); + } + { + auto unescaped=filesystem::unescape_argument("\\\\\\ \\ \\ \\\\"); + g_assert_cmpstr(unescaped.c_str(), ==, "\\ \\"); + } + { + auto unescaped=filesystem::unescape_argument("c:\\a\\ b\\c"); + g_assert_cmpstr(unescaped.c_str(), ==, "c:\\a b\\c"); + } + { + auto unescaped=filesystem::unescape_argument("\"\\\\\\\"\""); + g_assert_cmpstr(unescaped.c_str(), ==, "\\\""); + } + { + auto unescaped=filesystem::unescape_argument("\"\\\"\""); + g_assert_cmpstr(unescaped.c_str(), ==, "\""); + } + { + auto unescaped=filesystem::unescape_argument("\"a\\b\""); + g_assert_cmpstr(unescaped.c_str(), ==, "a\\b"); + } auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); { diff --git a/tests/lldb_test.cc b/tests/lldb_test.cc index 29ccfbc..01f5871 100644 --- a/tests/lldb_test.cc +++ b/tests/lldb_test.cc @@ -13,6 +13,67 @@ int main() { auto source_path=tests_path/"lldb_test_files"/"main.cpp"; g_assert(boost::filesystem::exists(source_path)); + { + auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("\"~/test/te st\""); + assert(std::get<0>(parsed_run_arguments).size()==0); + + assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); + + assert(std::get<2>(parsed_run_arguments).size()==0); + } + { + auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("~/test/te\\ st"); + assert(std::get<0>(parsed_run_arguments).size()==0); + + assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); + + assert(std::get<2>(parsed_run_arguments).size()==0); + } + { + auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("~/test/te\\ st Arg1\\\\ arg2"); + assert(std::get<0>(parsed_run_arguments).size()==0); + + assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); + + assert(std::get<2>(parsed_run_arguments).size()==2); + assert(std::get<2>(parsed_run_arguments)[0]=="Arg1\\"); + assert(std::get<2>(parsed_run_arguments)[1]=="arg2"); + } + { + auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("~/test/te\\ st Arg1\\\\\\ arg1"); + assert(std::get<0>(parsed_run_arguments).size()==0); + + assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); + + assert(std::get<2>(parsed_run_arguments).size()==1); + assert(std::get<2>(parsed_run_arguments)[0]=="Arg1\\ arg1"); + } + { + auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("\"~/test/te st\" Arg1 \"Ar g2\" Ar\\ g3"); + assert(std::get<0>(parsed_run_arguments).size()==0); + + assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); + + assert(std::get<2>(parsed_run_arguments).size()==3); + assert(std::get<2>(parsed_run_arguments)[0]=="Arg1"); + assert(std::get<2>(parsed_run_arguments)[1]=="Ar g2"); + assert(std::get<2>(parsed_run_arguments)[2]=="Ar g3"); + } + { + auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("ENV1=Test ENV2=Te\\ st ENV3=\"te ts\" ~/test/te\\ st Arg1 \"Ar g2\" Ar\\ g3"); + assert(std::get<0>(parsed_run_arguments).size()==3); + assert(std::get<0>(parsed_run_arguments)[0]=="ENV1=Test"); + assert(std::get<0>(parsed_run_arguments)[1]=="ENV2=Te st"); + assert(std::get<0>(parsed_run_arguments)[2]=="ENV3=te ts"); + + assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); + + assert(std::get<2>(parsed_run_arguments).size()==3); + assert(std::get<2>(parsed_run_arguments)[0]=="Arg1"); + assert(std::get<2>(parsed_run_arguments)[1]=="Ar g2"); + assert(std::get<2>(parsed_run_arguments)[2]=="Ar g3"); + } + std::vector > breakpoints; breakpoints.emplace_back(source_path, 2);