Browse Source

Debug start/continue: implemented environment parsing on string set in Debug Set Run Arguments

merge-requests/365/head
eidheim 8 years ago
parent
commit
55d056f16d
  1. 113
      src/debug_lldb.cc
  2. 3
      src/debug_lldb.h
  3. 20
      src/filesystem.cc
  4. 6
      src/source_spellcheck.cc
  5. 4
      src/window.cc
  6. 28
      tests/filesystem_test.cc
  7. 61
      tests/lldb_test.cc

113
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<std::pair<boost::filesystem::path, int> > &breakpoints,
std::function<void(int exit_status)> callback,
std::function<void(const std::string &status)> status_callback,
std::function<void(const boost::filesystem::path &file_path, int line_nr, int line_index)> stop_callback,
const std::string &remote_host) {
if(!debugger) {
lldb::SBDebugger::Initialize();
debugger=std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr));
listener=std::make_unique<lldb::SBListener>("juCi++ lldb listener");
}
//Create executable string and argument array
std::tuple<std::vector<std::string>, std::string, std::vector<std::string> > Debug::LLDB::parse_run_arguments(const std::string &command) {
std::vector<std::string> environment;
std::string executable;
std::vector<std::string> 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<argument.size();++c) {
if((argument[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<argument.size()) {
environment.emplace_back(argument.substr(0, c+1)+filesystem::unescape_argument(argument.substr(c+1)));
env_arg=true;
break;
}
else
break;
}
if(!env_arg) {
executable=filesystem::unescape_argument(argument);
#ifdef _WIN32
if(remote_host.empty())
executable+=".exe";
if(remote_host.empty())
executable+=".exe";
#endif
}
}
else
arguments.emplace_back(filesystem::unescape_argument(command.substr(start_pos, c-start_pos)));
arguments.emplace_back(filesystem::unescape_argument(argument));
start_pos=std::string::npos;
}
}
else if(command[c]=='\\' && !quote && !double_quote)
symbol=true;
else if(symbol)
symbol=false;
else if(command[c]=='\'' && !double_quote)
else if(command[c]=='\'' && backslash_count%2==0 && !double_quote)
quote=!quote;
else if(command[c]=='"' && !quote)
else if(command[c]=='"' && backslash_count%2==0 && !quote)
double_quote=!double_quote;
else if(command[c]=='\\' && !quote && !double_quote)
++backslash_count;
else
backslash_count=0;
if(c<command.size() && start_pos==std::string::npos && command[c]!=' ')
start_pos=c;
}
const char *argv[arguments.size()+1];
return std::make_tuple(environment, executable, arguments);
}
void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path,
const std::vector<std::pair<boost::filesystem::path, int> > &breakpoints,
std::function<void(int exit_status)> callback,
std::function<void(const std::string &status)> status_callback,
std::function<void(const boost::filesystem::path &file_path, int line_nr, int line_index)> stop_callback,
const std::string &remote_host) {
if(!debugger) {
lldb::SBDebugger::Initialize();
debugger=std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr));
listener=std::make_unique<lldb::SBListener>("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<const char*> argv;
for(size_t c=0;c<arguments.size();c++)
argv[c]=arguments[c].c_str();
argv[arguments.size()]=nullptr;
argv.emplace_back(arguments[c].c_str());
argv.emplace_back(nullptr);
auto target=debugger->CreateTarget(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<const char*> 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<lldb::SBProcess>(target.Launch(*listener, argv, const_cast<const char**>(environ), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error));
else {
// Create environment array
std::vector<const char*> 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<environ_size;++c)
environment.emplace_back(environ[c]);
environment.emplace_back(nullptr);
process = std::make_unique<lldb::SBProcess>(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)

3
src/debug_lldb.h

@ -8,6 +8,7 @@
#include <lldb/API/SBProcess.h>
#include <thread>
#include <mutex>
#include <tuple>
namespace Debug {
class LLDB {
@ -72,6 +73,8 @@ namespace Debug {
void write(const std::string &buffer);
private:
std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command);
std::unique_ptr<lldb::SBDebugger> debugger;
std::unique_ptr<lldb::SBListener> listener;
std::unique_ptr<lldb::SBProcess> process;

20
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<unescaped.size();++pos) {
if(unescaped[pos-1]=='\\' && unescaped[pos]==quotation_mark) {
size_t backslash_count=0;
for(size_t pos=0;pos<unescaped.size();++pos) {
if(backslash_count%2==1 && (unescaped[pos]=='\\' || unescaped[pos]==quotation_mark)) {
unescaped.erase(pos-1, 1);
--pos;
backslash_count=0;
}
else if(unescaped[pos]=='\\')
++backslash_count;
else
backslash_count=0;
}
return unescaped;
}
}
for(size_t pos=1;pos<unescaped.size();++pos) {
if(unescaped[pos-1]=='\\' && (unescaped[pos]==' ' || unescaped[pos]=='(' || unescaped[pos]==')' || unescaped[pos]=='\'' || unescaped[pos]=='"')) {
size_t backslash_count=0;
for(size_t pos=0;pos<unescaped.size();++pos) {
if(backslash_count%2==1 && (unescaped[pos]=='\\' || unescaped[pos]==' ' || unescaped[pos]=='(' || unescaped[pos]==')' || unescaped[pos]=='\'' || unescaped[pos]=='"')) {
unescaped.erase(pos-1, 1);
--pos;
backslash_count=0;
}
else if(unescaped[pos]=='\\')
++backslash_count;
else
backslash_count=0;
}
return unescaped;
}

6
src/source_spellcheck.cc

@ -412,10 +412,10 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
bool Source::SpellCheckView::is_word_iter(const Gtk::TextIter& iter) {
auto previous_iter=iter;
size_t forward_slash_count=0;
size_t backslash_count=0;
while(previous_iter.backward_char() && *previous_iter=='\\')
++forward_slash_count;
if(forward_slash_count%2==1)
++backslash_count;
if(backslash_count%2==1)
return false;
if(((*iter>='A' && *iter<='Z') || (*iter>='a' && *iter<='z') || *iter>=128))
return true;

4
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){

28
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);
{

61
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<std::pair<boost::filesystem::path, int> > breakpoints;
breakpoints.emplace_back(source_path, 2);

Loading…
Cancel
Save