2018-09-17 10:01:02 +00:00
# include "PostProcessor.hpp"
2019-01-18 09:16:53 +00:00
# include <boost/algorithm/string.hpp>
2018-12-10 08:52:22 +00:00
# include <boost/log/trivial.hpp>
2019-03-11 08:48:24 +00:00
# include <boost/format.hpp>
2019-01-17 19:34:19 +00:00
# include <boost/filesystem.hpp>
2019-03-27 11:14:34 +00:00
# include <boost/nowide/convert.hpp>
2018-12-10 08:52:22 +00:00
2018-12-09 21:47:49 +00:00
# ifdef WIN32
2018-09-17 10:01:02 +00:00
2019-01-17 19:34:19 +00:00
// The standard Windows includes.
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include <Windows.h>
2019-03-27 11:14:34 +00:00
# include <shellapi.h>
2018-09-17 13:32:54 +00:00
2019-01-17 19:34:19 +00:00
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
// This routine appends the given argument to a command line such that CommandLineToArgvW will return the argument string unchanged.
// Arguments in a command line should be separated by spaces; this function does not add these spaces.
// Argument - Supplies the argument to encode.
// CommandLine - Supplies the command line to which we append the encoded argument string.
static void quote_argv_winapi ( const std : : wstring & argument , std : : wstring & commmand_line_out )
2018-09-17 10:01:02 +00:00
{
2019-01-17 19:34:19 +00:00
// Don't quote unless we actually need to do so --- hopefully avoid problems if programs won't parse quotes properly.
if ( argument . empty ( ) = = false & & argument . find_first_of ( L " \t \n \v \" " ) = = argument . npos )
commmand_line_out . append ( argument ) ;
else {
commmand_line_out . push_back ( L ' " ' ) ;
for ( auto it = argument . begin ( ) ; ; + + it ) {
unsigned number_backslashes = 0 ;
while ( it ! = argument . end ( ) & & * it = = L ' \\ ' ) {
+ + it ;
+ + number_backslashes ;
}
if ( it = = argument . end ( ) ) {
// Escape all backslashes, but let the terminating double quotation mark we add below be interpreted as a metacharacter.
commmand_line_out . append ( number_backslashes * 2 , L ' \\ ' ) ;
break ;
} else if ( * it = = L ' " ' ) {
// Escape all backslashes and the following double quotation mark.
commmand_line_out . append ( number_backslashes * 2 + 1 , L ' \\ ' ) ;
commmand_line_out . push_back ( * it ) ;
} else {
// Backslashes aren't special here.
commmand_line_out . append ( number_backslashes , L ' \\ ' ) ;
commmand_line_out . push_back ( * it ) ;
}
}
commmand_line_out . push_back ( L ' " ' ) ;
}
2018-09-17 10:01:02 +00:00
}
2019-01-17 19:34:19 +00:00
static DWORD execute_process_winapi ( const std : : wstring & command_line )
{
// Extract the current environment to be passed to the child process.
std : : wstring envstr ;
{
wchar_t * env = GetEnvironmentStrings ( ) ;
assert ( env ! = nullptr ) ;
const wchar_t * var = env ;
size_t totallen = 0 ;
size_t len ;
while ( ( len = wcslen ( var ) ) > 0 ) {
totallen + = len + 1 ;
var + = len + 1 ;
}
envstr = std : : wstring ( env , totallen ) ;
FreeEnvironmentStrings ( env ) ;
}
2018-09-17 13:32:54 +00:00
2019-01-17 19:34:19 +00:00
STARTUPINFOW startup_info ;
memset ( & startup_info , 0 , sizeof ( startup_info ) ) ;
startup_info . cb = sizeof ( STARTUPINFO ) ;
#if 0
startup_info . dwFlags = STARTF_USESHOWWINDOW ;
startup_info . wShowWindow = SW_HIDE ;
# endif
PROCESS_INFORMATION process_info ;
if ( ! : : CreateProcessW (
nullptr /* lpApplicationName */ , ( LPWSTR ) command_line . c_str ( ) , nullptr /* lpProcessAttributes */ , nullptr /* lpThreadAttributes */ , false /* bInheritHandles */ ,
CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */ , ( LPVOID ) envstr . c_str ( ) , nullptr /* lpCurrentDirectory */ , & startup_info , & process_info ) )
throw std : : runtime_error ( std : : string ( " Failed starting the script " ) + boost : : nowide : : narrow ( command_line ) + " , Win32 error: " + std : : to_string ( int ( : : GetLastError ( ) ) ) ) ;
: : WaitForSingleObject ( process_info . hProcess , INFINITE ) ;
ULONG rc = 0 ;
: : GetExitCodeProcess ( process_info . hProcess , & rc ) ;
: : CloseHandle ( process_info . hThread ) ;
: : CloseHandle ( process_info . hProcess ) ;
return rc ;
}
// Run the script. If it is a perl script, run it through the bundled perl interpreter.
// If it is a batch file, run it through the cmd.exe.
// Otherwise run it directly.
2019-03-11 08:48:24 +00:00
static int run_script ( const std : : string & script , const std : : string & gcode , std : : string & /*std_err*/ )
2019-01-17 19:34:19 +00:00
{
// Unpack the argument list provided by the user.
int nArgs ;
LPWSTR * szArglist = CommandLineToArgvW ( boost : : nowide : : widen ( script ) . c_str ( ) , & nArgs ) ;
if ( szArglist = = nullptr | | nArgs < = 0 ) {
// CommandLineToArgvW failed. Maybe the command line escapment is invalid?
throw std : : runtime_error ( std : : string ( " Post processing script " ) + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path. " ) ;
}
2018-09-17 10:01:02 +00:00
2019-01-17 19:34:19 +00:00
std : : wstring command_line ;
std : : wstring command = szArglist [ 0 ] ;
if ( ! boost : : filesystem : : exists ( boost : : filesystem : : path ( command ) ) )
throw std : : runtime_error ( std : : string ( " The configured post-processing script does not exist: " ) + boost : : nowide : : narrow ( command ) ) ;
if ( boost : : iends_with ( command , L " .pl " ) ) {
// This is a perl script. Run it through the perl interpreter.
// The current process may be slic3r.exe or slic3r-console.exe.
// Find the path of the process:
wchar_t wpath_exe [ _MAX_PATH + 1 ] ;
: : GetModuleFileNameW ( nullptr , wpath_exe , _MAX_PATH ) ;
boost : : filesystem : : path path_exe ( wpath_exe ) ;
boost : : filesystem : : path path_perl = path_exe . parent_path ( ) / " perl " / " perl.exe " ;
if ( ! boost : : filesystem : : exists ( path_perl ) ) {
LocalFree ( szArglist ) ;
throw std : : runtime_error ( std : : string ( " Perl interpreter " ) + path_perl . string ( ) + " does not exist. " ) ;
}
// Replace it with the current perl interpreter.
quote_argv_winapi ( boost : : nowide : : widen ( path_perl . string ( ) ) , command_line ) ;
command_line + = L " " ;
} else if ( boost : : iends_with ( command , " .bat " ) ) {
// Run a batch file through the command line interpreter.
command_line = L " cmd.exe /C " ;
}
for ( int i = 0 ; i < nArgs ; + + i ) {
quote_argv_winapi ( szArglist [ i ] , command_line ) ;
command_line + = L " " ;
}
LocalFree ( szArglist ) ;
quote_argv_winapi ( boost : : nowide : : widen ( gcode ) , command_line ) ;
return ( int ) execute_process_winapi ( command_line ) ;
}
# else
2019-03-11 08:48:24 +00:00
// POSIX
# include <cstdlib> // getenv()
# include <sstream>
# include <boost/process.hpp>
namespace process = boost : : process ;
static int run_script ( const std : : string & script , const std : : string & gcode , std : : string & std_err )
{
// Try to obtain user's default shell
const char * shell = : : getenv ( " SHELL " ) ;
if ( shell = = nullptr ) { shell = " sh " ; }
// Quote and escape the gcode path argument
std : : string command { script } ;
command . append ( " ' " ) ;
for ( char c : gcode ) {
if ( c = = ' \' ' ) { command . append ( " ' \\ '' " ) ; }
else { command . push_back ( c ) ; }
}
command . push_back ( ' \' ' ) ;
BOOST_LOG_TRIVIAL ( debug ) < < boost : : format ( " Executing script, shell: %1%, command: %2% " ) % shell % command ;
process : : ipstream istd_err ;
process : : child child ( shell , " -c " , command , process : : std_err > istd_err ) ;
std_err . clear ( ) ;
std : : string line ;
while ( child . running ( ) & & std : : getline ( istd_err , line ) ) {
std_err . append ( line ) ;
std_err . push_back ( ' \n ' ) ;
}
child . wait ( ) ;
return child . exit_code ( ) ;
}
2018-12-09 21:47:49 +00:00
# endif
2018-09-17 10:01:02 +00:00
2018-09-17 13:32:54 +00:00
namespace Slic3r {
2018-09-17 10:01:02 +00:00
void run_post_process_scripts ( const std : : string & path , const PrintConfig & config )
{
if ( config . post_process . values . empty ( ) )
return ;
2018-12-10 08:52:22 +00:00
config . setenv_ ( ) ;
2018-12-09 21:47:49 +00:00
auto gcode_file = boost : : filesystem : : path ( path ) ;
2018-12-10 08:52:22 +00:00
if ( ! boost : : filesystem : : exists ( gcode_file ) )
2018-12-09 21:47:49 +00:00
throw std : : runtime_error ( std : : string ( " Post-processor can't find exported gcode file " ) ) ;
2019-01-17 19:34:19 +00:00
for ( const std : : string & scripts : config . post_process . values ) {
std : : vector < std : : string > lines ;
boost : : split ( lines , scripts , boost : : is_any_of ( " \r \n " ) ) ;
for ( std : : string script : lines ) {
// Ignore empty post processing script lines.
boost : : trim ( script ) ;
if ( script . empty ( ) )
continue ;
BOOST_LOG_TRIVIAL ( info ) < < " Executing script " < < script < < " on file " < < path ;
2019-03-11 08:48:24 +00:00
std : : string std_err ;
const int result = run_script ( script , gcode_file . string ( ) , std_err ) ;
if ( result ! = 0 ) {
const std : : string msg = std_err . empty ( ) ? ( boost : : format ( " Post-processing script %1% on file %2% failed. \n Error code: %3% " ) % script % path % result ) . str ( )
: ( boost : : format ( " Post-processing script %1% on file %2% failed. \n Error code: %3% \n Output: \n %4% " ) % script % path % result % std_err ) . str ( ) ;
BOOST_LOG_TRIVIAL ( error ) < < msg ;
throw std : : runtime_error ( msg ) ;
}
2019-01-17 19:34:19 +00:00
}
2018-09-17 10:01:02 +00:00
}
}
} // namespace Slic3r