From 3a8fc4dd829b2c03a7b5b573ba753a3f897662da Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Sun, 21 Sep 2025 16:15:25 -0700 Subject: [PATCH] Fully separate command and parameters Ensures proper execution on all platforms Fixes https://gitlab.com/kicad/code/kicad/issues/14261 (cherry picked from commit 25e9464d2c2252706ddc9ec906a1664dcd69aaf5) --- eeschema/dialogs/dialog_export_netlist.cpp | 192 ++++++++++++++++----- 1 file changed, 148 insertions(+), 44 deletions(-) diff --git a/eeschema/dialogs/dialog_export_netlist.cpp b/eeschema/dialogs/dialog_export_netlist.cpp index 32717a7d75..7dd672bd2f 100644 --- a/eeschema/dialogs/dialog_export_netlist.cpp +++ b/eeschema/dialogs/dialog_export_netlist.cpp @@ -53,10 +53,63 @@ #include #include #include +#include #include #include +#include #include +#include + + +namespace +{ +std::vector SplitCommandLine( const wxString& aCommand ) +{ + std::vector args; + wxString current; + bool inSingle = false; + bool inDouble = false; + bool argStarted = false; + + for( wxUniChar c : aCommand ) + { + if( c == '"' && !inSingle ) + { + inDouble = !inDouble; + argStarted = true; + continue; + } + + if( c == '\'' && !inDouble ) + { + inSingle = !inSingle; + argStarted = true; + continue; + } + + if( ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) && !inSingle && !inDouble ) + { + if( argStarted || !current.IsEmpty() ) + { + args.emplace_back( current ); + current.clear(); + argStarted = false; + } + + continue; + } + + current.Append( c ); + argStarted = true; + } + + if( argStarted || !current.IsEmpty() ) + args.emplace_back( current ); + + return args; +} +} // namespace #define CUSTOMPANEL_COUNTMAX 8 // Max number of netlist plugins @@ -616,68 +669,119 @@ bool DIALOG_EXPORT_NETLIST::TransferDataFromWindow() if( !commandLine.IsEmpty() ) { - wxProcess* process = new wxProcess( GetEventHandler(), wxID_ANY ); - process->Redirect(); - wxExecute( commandLine, wxEXEC_ASYNC, process ); + std::vector argsStrings = SplitCommandLine( commandLine ); - reporter.ReportHead( commandLine, RPT_SEVERITY_ACTION ); - process->Activate(); + if( !argsStrings.empty() ) + { + std::vector argv; + argv.reserve( argsStrings.size() + 1 ); - std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); // give the process time to start and output any data or errors + for( wxString& arg : argsStrings ) + argv.emplace_back( arg.wc_str() ); - if( process->IsInputAvailable() ) - { - wxInputStream* in = process->GetInputStream(); - wxTextInputStream textstream( *in ); + argv.emplace_back( nullptr ); - while( in->CanRead() ) - { - wxString line = textstream.ReadLine(); + wxExecuteEnv env; + wxGetEnvMap( &env.env ); + + const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables(); - if( !line.IsEmpty() ) - reporter.Report( line, RPT_SEVERITY_INFO ); + for( const auto& [key, value] : envVars ) + { + if( !value.GetValue().IsEmpty() ) + env.env[key] = value.GetValue(); } - } - if( process->IsErrorAvailable() ) - { - wxInputStream* err = process->GetErrorStream(); - wxTextInputStream textstream( *err ); + wxFileName netlistFile( fullpath ); + + if( !netlistFile.IsAbsolute() ) + netlistFile.MakeAbsolute( Prj().GetProjectPath() ); + else + netlistFile.MakeAbsolute(); + + wxString cwd = netlistFile.GetPath(); + + if( cwd.IsEmpty() ) + cwd = Prj().GetProjectPath(); + + env.cwd = cwd; + + wxProcess* process = new wxProcess( GetEventHandler(), wxID_ANY ); + process->Redirect(); - while( err->CanRead() ) + long pid = wxExecute( argv.data(), wxEXEC_ASYNC, process, &env ); + + reporter.ReportHead( commandLine, RPT_SEVERITY_ACTION ); + + if( pid <= 0 ) + { + reporter.Report( _( "external simulator not found" ), RPT_SEVERITY_ERROR ); + reporter.Report( _( "Note: command line is usually: " + "<path to SPICE binary> \"%I\"" ), + RPT_SEVERITY_INFO ); + delete process; + } + else { - wxString line = textstream.ReadLine(); + process->Activate(); + + std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); // give the process time to start and output any data or errors - if( !line.IsEmpty() ) + if( process->IsInputAvailable() ) { - if( line.EndsWith( wxS( "failed with error 2!" ) ) ) // ENOENT - { - reporter.Report( _( "external simulator not found" ), RPT_SEVERITY_ERROR ); - reporter.Report( _( "Note: command line is usually: " - "<path to SPICE binary> \"%I\"" ), - RPT_SEVERITY_INFO ); - } - else if( line.EndsWith( wxS( "failed with error 8!" ) ) ) // ENOEXEC - { - reporter.Report( _( "external simulator has the wrong format or " - "architecture" ), RPT_SEVERITY_ERROR ); - } - else if( line.EndsWith( "failed with error 13!" ) ) // EACCES + wxInputStream* in = process->GetInputStream(); + wxTextInputStream textstream( *in ); + + while( in->CanRead() ) { - reporter.Report( _( "permission denied" ), RPT_SEVERITY_ERROR ); + wxString line = textstream.ReadLine(); + + if( !line.IsEmpty() ) + reporter.Report( line, RPT_SEVERITY_INFO ); } - else + } + + if( process->IsErrorAvailable() ) + { + wxInputStream* err = process->GetErrorStream(); + wxTextInputStream textstream( *err ); + + while( err->CanRead() ) { - reporter.Report( line, RPT_SEVERITY_ERROR ); + wxString line = textstream.ReadLine(); + + if( !line.IsEmpty() ) + { + if( line.EndsWith( wxS( "failed with error 2!" ) ) ) // ENOENT + { + reporter.Report( _( "external simulator not found" ), RPT_SEVERITY_ERROR ); + reporter.Report( _( "Note: command line is usually: " + "<path to SPICE binary> \"%I\"" ), + RPT_SEVERITY_INFO ); + } + else if( line.EndsWith( wxS( "failed with error 8!" ) ) ) // ENOEXEC + { + reporter.Report( _( "external simulator has the wrong format or " + "architecture" ), RPT_SEVERITY_ERROR ); + } + else if( line.EndsWith( "failed with error 13!" ) ) // EACCES + { + reporter.Report( _( "permission denied" ), RPT_SEVERITY_ERROR ); + } + else + { + reporter.Report( line, RPT_SEVERITY_ERROR ); + } + } } } - } - } - process->CloseOutput(); - process->Detach(); + process->CloseOutput(); + process->Detach(); - // Do not delete process, it will delete itself when it terminates + // Do not delete process, it will delete itself when it terminates + } + } } }