2018-05-03 19:45:43 +00:00
# include "Utils.hpp"
2018-06-20 11:57:37 +00:00
# include "I18N.hpp"
2018-05-03 19:45:43 +00:00
2017-08-03 15:31:31 +00:00
# include <locale>
2017-10-30 17:15:41 +00:00
# include <ctime>
2018-11-19 10:07:39 +00:00
# include <cstdarg>
# include <stdio.h>
2017-08-03 15:31:31 +00:00
2019-09-11 10:13:59 +00:00
# include "Time.hpp"
2018-04-20 09:05:00 +00:00
# ifdef WIN32
2019-07-17 13:48:53 +00:00
# include <windows.h>
# include <psapi.h>
2018-04-20 09:05:00 +00:00
# else
2019-07-17 13:48:53 +00:00
# include <unistd.h>
# include <sys/types.h>
# include <sys/param.h>
2019-08-05 11:01:23 +00:00
# include <sys/resource.h>
2019-07-17 13:48:53 +00:00
# ifdef BSD
# include <sys/sysctl.h>
# endif
2019-08-05 11:01:23 +00:00
# ifdef __APPLE__
# include <mach/mach.h>
# endif
2018-04-20 09:05:00 +00:00
# endif
2016-12-12 18:13:33 +00:00
# include <boost/log/core.hpp>
# include <boost/log/trivial.hpp>
# include <boost/log/expressions.hpp>
2017-08-03 15:31:31 +00:00
# include <boost/locale.hpp>
2017-10-25 10:53:31 +00:00
# include <boost/algorithm/string/predicate.hpp>
2017-10-17 18:00:15 +00:00
# include <boost/filesystem.hpp>
2017-12-21 15:56:33 +00:00
# include <boost/filesystem/path.hpp>
# include <boost/nowide/fstream.hpp>
2017-08-03 15:31:31 +00:00
# include <boost/nowide/convert.hpp>
2018-09-14 07:28:00 +00:00
# include <boost/nowide/cstdio.hpp>
2017-08-03 15:31:31 +00:00
2018-09-17 13:12:13 +00:00
# include <tbb/task_scheduler_init.h>
2017-08-03 15:31:31 +00:00
2019-02-03 14:30:37 +00:00
# if defined(__linux) || defined(__GNUC__ )
# include <strings.h>
# endif /* __linux */
# ifdef _MSC_VER
# define strcasecmp _stricmp
# endif
2018-08-09 19:15:49 +00:00
2016-12-12 18:13:33 +00:00
namespace Slic3r {
2017-03-03 13:38:25 +00:00
static boost : : log : : trivial : : severity_level logSeverity = boost : : log : : trivial : : error ;
2016-12-12 18:13:33 +00:00
2019-02-15 12:40:23 +00:00
static boost : : log : : trivial : : severity_level level_to_boost ( unsigned level )
2016-12-12 18:13:33 +00:00
{
switch ( level ) {
2017-03-03 11:53:05 +00:00
// Report fatal errors only.
2019-02-15 12:40:23 +00:00
case 0 : return boost : : log : : trivial : : fatal ;
2017-03-03 11:53:05 +00:00
// Report fatal errors and errors.
2019-02-15 12:40:23 +00:00
case 1 : return boost : : log : : trivial : : error ;
2017-03-03 11:53:05 +00:00
// Report fatal errors, errors and warnings.
2019-02-15 12:40:23 +00:00
case 2 : return boost : : log : : trivial : : warning ;
2017-03-03 11:53:05 +00:00
// Report all errors, warnings and infos.
2019-02-15 12:40:23 +00:00
case 3 : return boost : : log : : trivial : : info ;
2017-03-03 11:53:05 +00:00
// Report all errors, warnings, infos and debugging.
2019-02-15 12:40:23 +00:00
case 4 : return boost : : log : : trivial : : debug ;
2017-03-03 11:53:05 +00:00
// Report everyting including fine level tracing information.
2019-02-15 12:40:23 +00:00
default : return boost : : log : : trivial : : trace ;
2016-12-12 18:13:33 +00:00
}
2019-02-15 12:40:23 +00:00
}
void set_logging_level ( unsigned int level )
{
logSeverity = level_to_boost ( level ) ;
2016-12-12 18:13:33 +00:00
boost : : log : : core : : get ( ) - > set_filter
(
boost : : log : : trivial : : severity > = logSeverity
) ;
}
2018-12-18 17:40:35 +00:00
unsigned get_logging_level ( )
{
switch ( logSeverity ) {
case boost : : log : : trivial : : fatal : return 0 ;
case boost : : log : : trivial : : error : return 1 ;
case boost : : log : : trivial : : warning : return 2 ;
case boost : : log : : trivial : : info : return 3 ;
case boost : : log : : trivial : : debug : return 4 ;
2019-02-15 12:40:23 +00:00
case boost : : log : : trivial : : trace : return 5 ;
2018-12-18 17:40:35 +00:00
default : return 1 ;
}
}
2019-08-08 13:24:23 +00:00
// Force set_logging_level(<=error) after loading of the DLL.
// This is currently only needed if libslic3r is loaded as a shared library into Perl interpreter
// to perform unit and integration tests.
static struct RunOnInit {
RunOnInit ( ) {
set_logging_level ( 1 ) ;
}
} g_RunOnInit ;
2017-03-03 11:53:05 +00:00
void trace ( unsigned int level , const char * message )
{
2019-02-15 12:40:23 +00:00
boost : : log : : trivial : : severity_level severity = level_to_boost ( level ) ;
2017-03-03 11:53:05 +00:00
BOOST_LOG_STREAM_WITH_PARAMS ( : : boost : : log : : trivial : : logger : : get ( ) , \
( : : boost : : log : : keywords : : severity = severity ) ) < < message ;
}
2018-08-09 19:15:49 +00:00
void disable_multi_threading ( )
{
// Disable parallelization so the Shiny profiler works
static tbb : : task_scheduler_init * tbb_init = nullptr ;
if ( tbb_init = = nullptr )
tbb_init = new tbb : : task_scheduler_init ( 1 ) ;
}
2017-10-17 18:00:15 +00:00
static std : : string g_var_dir ;
void set_var_dir ( const std : : string & dir )
{
g_var_dir = dir ;
}
const std : : string & var_dir ( )
{
return g_var_dir ;
}
std : : string var ( const std : : string & file_name )
{
2018-05-23 11:19:25 +00:00
auto file = ( boost : : filesystem : : path ( g_var_dir ) / file_name ) . make_preferred ( ) ;
2017-10-17 18:00:15 +00:00
return file . string ( ) ;
}
2017-12-10 12:19:44 +00:00
static std : : string g_resources_dir ;
void set_resources_dir ( const std : : string & dir )
{
g_resources_dir = dir ;
}
const std : : string & resources_dir ( )
{
return g_resources_dir ;
}
2018-02-12 07:57:32 +00:00
static std : : string g_local_dir ;
void set_local_dir ( const std : : string & dir )
{
g_local_dir = dir ;
}
const std : : string & localization_dir ( )
2018-02-07 16:13:52 +00:00
{
2018-02-12 07:57:32 +00:00
return g_local_dir ;
2018-02-07 16:13:52 +00:00
}
2018-06-20 11:57:37 +00:00
// Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
2018-06-20 16:33:46 +00:00
Slic3r : : I18N : : translate_fn_type Slic3r : : I18N : : translate_fn = nullptr ;
2018-06-20 11:57:37 +00:00
2017-10-25 10:53:31 +00:00
static std : : string g_data_dir ;
void set_data_dir ( const std : : string & dir )
{
2018-04-13 13:08:58 +00:00
g_data_dir = dir ;
2017-10-25 10:53:31 +00:00
}
const std : : string & data_dir ( )
{
return g_data_dir ;
}
2019-08-20 14:19:30 +00:00
# ifdef _WIN32
// The following helpers are borrowed from the LLVM project https://github.com/llvm
namespace WindowsSupport
{
template < typename HandleTraits >
class ScopedHandle {
typedef typename HandleTraits : : handle_type handle_type ;
handle_type Handle ;
ScopedHandle ( const ScopedHandle & other ) = delete ;
void operator = ( const ScopedHandle & other ) = delete ;
public :
ScopedHandle ( ) : Handle ( HandleTraits : : GetInvalid ( ) ) { }
explicit ScopedHandle ( handle_type h ) : Handle ( h ) { }
~ ScopedHandle ( ) { if ( HandleTraits : : IsValid ( Handle ) ) HandleTraits : : Close ( Handle ) ; }
handle_type take ( ) {
handle_type t = Handle ;
Handle = HandleTraits : : GetInvalid ( ) ;
return t ;
}
ScopedHandle & operator = ( handle_type h ) {
if ( HandleTraits : : IsValid ( Handle ) )
HandleTraits : : Close ( Handle ) ;
Handle = h ;
return * this ;
}
// True if Handle is valid.
explicit operator bool ( ) const { return HandleTraits : : IsValid ( Handle ) ? true : false ; }
operator handle_type ( ) const { return Handle ; }
} ;
struct CommonHandleTraits {
typedef HANDLE handle_type ;
static handle_type GetInvalid ( ) { return INVALID_HANDLE_VALUE ; }
static void Close ( handle_type h ) { : : CloseHandle ( h ) ; }
static bool IsValid ( handle_type h ) { return h ! = GetInvalid ( ) ; }
} ;
typedef ScopedHandle < CommonHandleTraits > ScopedFileHandle ;
std : : error_code map_windows_error ( unsigned windows_error_code )
{
# define MAP_ERR_TO_COND(x, y) case x: return std::make_error_code(std::errc::y)
switch ( windows_error_code ) {
MAP_ERR_TO_COND ( ERROR_ACCESS_DENIED , permission_denied ) ;
MAP_ERR_TO_COND ( ERROR_ALREADY_EXISTS , file_exists ) ;
MAP_ERR_TO_COND ( ERROR_BAD_UNIT , no_such_device ) ;
MAP_ERR_TO_COND ( ERROR_BUFFER_OVERFLOW , filename_too_long ) ;
MAP_ERR_TO_COND ( ERROR_BUSY , device_or_resource_busy ) ;
MAP_ERR_TO_COND ( ERROR_BUSY_DRIVE , device_or_resource_busy ) ;
MAP_ERR_TO_COND ( ERROR_CANNOT_MAKE , permission_denied ) ;
MAP_ERR_TO_COND ( ERROR_CANTOPEN , io_error ) ;
MAP_ERR_TO_COND ( ERROR_CANTREAD , io_error ) ;
MAP_ERR_TO_COND ( ERROR_CANTWRITE , io_error ) ;
MAP_ERR_TO_COND ( ERROR_CURRENT_DIRECTORY , permission_denied ) ;
MAP_ERR_TO_COND ( ERROR_DEV_NOT_EXIST , no_such_device ) ;
MAP_ERR_TO_COND ( ERROR_DEVICE_IN_USE , device_or_resource_busy ) ;
MAP_ERR_TO_COND ( ERROR_DIR_NOT_EMPTY , directory_not_empty ) ;
MAP_ERR_TO_COND ( ERROR_DIRECTORY , invalid_argument ) ;
MAP_ERR_TO_COND ( ERROR_DISK_FULL , no_space_on_device ) ;
MAP_ERR_TO_COND ( ERROR_FILE_EXISTS , file_exists ) ;
MAP_ERR_TO_COND ( ERROR_FILE_NOT_FOUND , no_such_file_or_directory ) ;
MAP_ERR_TO_COND ( ERROR_HANDLE_DISK_FULL , no_space_on_device ) ;
MAP_ERR_TO_COND ( ERROR_INVALID_ACCESS , permission_denied ) ;
MAP_ERR_TO_COND ( ERROR_INVALID_DRIVE , no_such_device ) ;
MAP_ERR_TO_COND ( ERROR_INVALID_FUNCTION , function_not_supported ) ;
MAP_ERR_TO_COND ( ERROR_INVALID_HANDLE , invalid_argument ) ;
MAP_ERR_TO_COND ( ERROR_INVALID_NAME , invalid_argument ) ;
MAP_ERR_TO_COND ( ERROR_LOCK_VIOLATION , no_lock_available ) ;
MAP_ERR_TO_COND ( ERROR_LOCKED , no_lock_available ) ;
MAP_ERR_TO_COND ( ERROR_NEGATIVE_SEEK , invalid_argument ) ;
MAP_ERR_TO_COND ( ERROR_NOACCESS , permission_denied ) ;
MAP_ERR_TO_COND ( ERROR_NOT_ENOUGH_MEMORY , not_enough_memory ) ;
MAP_ERR_TO_COND ( ERROR_NOT_READY , resource_unavailable_try_again ) ;
MAP_ERR_TO_COND ( ERROR_OPEN_FAILED , io_error ) ;
MAP_ERR_TO_COND ( ERROR_OPEN_FILES , device_or_resource_busy ) ;
MAP_ERR_TO_COND ( ERROR_OUTOFMEMORY , not_enough_memory ) ;
MAP_ERR_TO_COND ( ERROR_PATH_NOT_FOUND , no_such_file_or_directory ) ;
MAP_ERR_TO_COND ( ERROR_BAD_NETPATH , no_such_file_or_directory ) ;
MAP_ERR_TO_COND ( ERROR_READ_FAULT , io_error ) ;
MAP_ERR_TO_COND ( ERROR_RETRY , resource_unavailable_try_again ) ;
MAP_ERR_TO_COND ( ERROR_SEEK , io_error ) ;
MAP_ERR_TO_COND ( ERROR_SHARING_VIOLATION , permission_denied ) ;
MAP_ERR_TO_COND ( ERROR_TOO_MANY_OPEN_FILES , too_many_files_open ) ;
MAP_ERR_TO_COND ( ERROR_WRITE_FAULT , io_error ) ;
MAP_ERR_TO_COND ( ERROR_WRITE_PROTECT , permission_denied ) ;
MAP_ERR_TO_COND ( WSAEACCES , permission_denied ) ;
MAP_ERR_TO_COND ( WSAEBADF , bad_file_descriptor ) ;
MAP_ERR_TO_COND ( WSAEFAULT , bad_address ) ;
MAP_ERR_TO_COND ( WSAEINTR , interrupted ) ;
MAP_ERR_TO_COND ( WSAEINVAL , invalid_argument ) ;
MAP_ERR_TO_COND ( WSAEMFILE , too_many_files_open ) ;
MAP_ERR_TO_COND ( WSAENAMETOOLONG , filename_too_long ) ;
default :
return std : : error_code ( windows_error_code , std : : system_category ( ) ) ;
}
# undef MAP_ERR_TO_COND
}
static std : : error_code rename_internal ( HANDLE from_handle , const std : : wstring & wide_to , bool replace_if_exists )
{
std : : vector < char > rename_info_buf ( sizeof ( FILE_RENAME_INFO ) - sizeof ( wchar_t ) + ( wide_to . size ( ) * sizeof ( wchar_t ) ) ) ;
FILE_RENAME_INFO & rename_info = * reinterpret_cast < FILE_RENAME_INFO * > ( rename_info_buf . data ( ) ) ;
rename_info . ReplaceIfExists = replace_if_exists ;
rename_info . RootDirectory = 0 ;
rename_info . FileNameLength = DWORD ( wide_to . size ( ) * sizeof ( wchar_t ) ) ;
std : : copy ( wide_to . begin ( ) , wide_to . end ( ) , & rename_info . FileName [ 0 ] ) ;
: : SetLastError ( ERROR_SUCCESS ) ;
if ( ! : : SetFileInformationByHandle ( from_handle , FileRenameInfo , & rename_info , ( DWORD ) rename_info_buf . size ( ) ) ) {
unsigned Error = GetLastError ( ) ;
if ( Error = = ERROR_SUCCESS )
Error = ERROR_CALL_NOT_IMPLEMENTED ; // Wine doesn't always set error code.
return map_windows_error ( Error ) ;
}
return std : : error_code ( ) ;
}
static std : : error_code real_path_from_handle ( HANDLE H , std : : wstring & buffer )
{
buffer . resize ( MAX_PATH + 1 ) ;
DWORD CountChars = : : GetFinalPathNameByHandleW ( H , ( LPWSTR ) buffer . data ( ) , ( DWORD ) buffer . size ( ) - 1 , FILE_NAME_NORMALIZED ) ;
if ( CountChars > buffer . size ( ) ) {
// The buffer wasn't big enough, try again. In this case the return value *does* indicate the size of the null terminator.
buffer . resize ( ( size_t ) CountChars ) ;
CountChars = : : GetFinalPathNameByHandleW ( H , ( LPWSTR ) buffer . data ( ) , ( DWORD ) buffer . size ( ) - 1 , FILE_NAME_NORMALIZED ) ;
}
if ( CountChars = = 0 )
return map_windows_error ( GetLastError ( ) ) ;
buffer . resize ( CountChars ) ;
return std : : error_code ( ) ;
}
std : : error_code rename ( const std : : string & from , const std : : string & to )
{
// Convert to utf-16.
std : : wstring wide_from = boost : : nowide : : widen ( from ) ;
std : : wstring wide_to = boost : : nowide : : widen ( to ) ;
ScopedFileHandle from_handle ;
// Retry this a few times to defeat badly behaved file system scanners.
for ( unsigned retry = 0 ; retry ! = 200 ; + + retry ) {
if ( retry ! = 0 )
: : Sleep ( 10 ) ;
from_handle = : : CreateFileW ( ( LPWSTR ) wide_from . data ( ) , GENERIC_READ | DELETE , FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( from_handle )
break ;
}
if ( ! from_handle )
return map_windows_error ( GetLastError ( ) ) ;
// We normally expect this loop to succeed after a few iterations. If it
// requires more than 200 tries, it's more likely that the failures are due to
// a true error, so stop trying.
for ( unsigned retry = 0 ; retry ! = 200 ; + + retry ) {
auto errcode = rename_internal ( from_handle , wide_to , true ) ;
if ( errcode = = std : : error_code ( ERROR_CALL_NOT_IMPLEMENTED , std : : system_category ( ) ) ) {
// Wine doesn't support SetFileInformationByHandle in rename_internal.
// Fall back to MoveFileEx.
if ( std : : error_code errcode2 = real_path_from_handle ( from_handle , wide_from ) )
return errcode2 ;
if ( : : MoveFileExW ( ( LPCWSTR ) wide_from . data ( ) , ( LPCWSTR ) wide_to . data ( ) , MOVEFILE_REPLACE_EXISTING ) )
return std : : error_code ( ) ;
return map_windows_error ( GetLastError ( ) ) ;
}
if ( ! errcode | | errcode ! = std : : errc : : permission_denied )
return errcode ;
// The destination file probably exists and is currently open in another
// process, either because the file was opened without FILE_SHARE_DELETE or
// it is mapped into memory (e.g. using MemoryBuffer). Rename it in order to
// move it out of the way of the source file. Use FILE_FLAG_DELETE_ON_CLOSE
// to arrange for the destination file to be deleted when the other process
// closes it.
ScopedFileHandle to_handle ( : : CreateFileW ( ( LPCWSTR ) wide_to . data ( ) , GENERIC_READ | DELETE , FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE , NULL ) ) ;
if ( ! to_handle ) {
auto errcode = map_windows_error ( GetLastError ( ) ) ;
// Another process might have raced with us and moved the existing file
// out of the way before we had a chance to open it. If that happens, try
// to rename the source file again.
if ( errcode = = std : : errc : : no_such_file_or_directory )
continue ;
return errcode ;
}
BY_HANDLE_FILE_INFORMATION FI ;
if ( ! : : GetFileInformationByHandle ( to_handle , & FI ) )
return map_windows_error ( GetLastError ( ) ) ;
// Try to find a unique new name for the destination file.
for ( unsigned unique_id = 0 ; unique_id ! = 200 ; + + unique_id ) {
std : : wstring tmp_filename = wide_to + L " .tmp " + std : : to_wstring ( unique_id ) ;
std : : error_code errcode = rename_internal ( to_handle , tmp_filename , false ) ;
if ( errcode ) {
if ( errcode = = std : : make_error_code ( std : : errc : : file_exists ) | | errcode = = std : : make_error_code ( std : : errc : : permission_denied ) ) {
// Again, another process might have raced with us and moved the file
// before we could move it. Check whether this is the case, as it
// might have caused the permission denied error. If that was the
// case, we don't need to move it ourselves.
ScopedFileHandle to_handle2 ( : : CreateFileW ( ( LPCWSTR ) wide_to . data ( ) , 0 , FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL ) ) ;
if ( ! to_handle2 ) {
auto errcode = map_windows_error ( GetLastError ( ) ) ;
if ( errcode = = std : : errc : : no_such_file_or_directory )
break ;
return errcode ;
}
BY_HANDLE_FILE_INFORMATION FI2 ;
if ( ! : : GetFileInformationByHandle ( to_handle2 , & FI2 ) )
return map_windows_error ( GetLastError ( ) ) ;
if ( FI . nFileIndexHigh ! = FI2 . nFileIndexHigh | | FI . nFileIndexLow ! = FI2 . nFileIndexLow | | FI . dwVolumeSerialNumber ! = FI2 . dwVolumeSerialNumber )
break ;
continue ;
}
return errcode ;
}
break ;
}
// Okay, the old destination file has probably been moved out of the way at
// this point, so try to rename the source file again. Still, another
// process might have raced with us to create and open the destination
// file, so we need to keep doing this until we succeed.
}
// The most likely root cause.
return std : : make_error_code ( std : : errc : : permission_denied ) ;
}
} // namespace WindowsSupport
# endif /* _WIN32 */
2016-12-12 18:13:33 +00:00
2018-09-14 07:28:00 +00:00
// borrowed from LVVM lib/Support/Windows/Path.inc
2019-08-20 14:19:30 +00:00
std : : error_code rename_file ( const std : : string & from , const std : : string & to )
2013-09-09 19:40:57 +00:00
{
2018-09-14 07:28:00 +00:00
# ifdef _WIN32
2019-08-20 14:19:30 +00:00
return WindowsSupport : : rename ( from , to ) ;
2018-09-14 07:28:00 +00:00
# else
2018-09-17 15:17:38 +00:00
boost : : nowide : : remove ( to . c_str ( ) ) ;
2019-08-20 14:19:30 +00:00
return std : : make_error_code ( static_cast < std : : errc > ( boost : : nowide : : rename ( from . c_str ( ) , to . c_str ( ) ) ) ) ;
2018-09-14 07:28:00 +00:00
# endif
2018-05-03 19:45:43 +00:00
}
2020-10-23 16:34:19 +00:00
CopyFileResult copy_file_inner ( const std : : string & from , const std : : string & to , std : : string & error_message )
2019-12-19 14:36:00 +00:00
{
const boost : : filesystem : : path source ( from ) ;
const boost : : filesystem : : path target ( to ) ;
static const auto perms = boost : : filesystem : : owner_read | boost : : filesystem : : owner_write | boost : : filesystem : : group_read | boost : : filesystem : : others_read ; // aka 644
// Make sure the file has correct permission both before and after we copy over it.
// NOTE: error_code variants are used here to supress expception throwing.
// Error code of permission() calls is ignored on purpose - if they fail,
// the copy_file() function will fail appropriately and we don't want the permission()
// calls to cause needless failures on permissionless filesystems (ie. FATs on SD cards etc.)
// or when the target file doesn't exist.
boost : : system : : error_code ec ;
2020-10-23 13:36:14 +00:00
boost : : filesystem : : permissions ( target , perms , ec ) ;
2020-11-04 08:23:47 +00:00
if ( ec )
2020-11-26 16:15:26 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " < < ec . message ( ) ;
2020-10-23 16:34:19 +00:00
ec . clear ( ) ;
boost : : filesystem : : copy_file ( source , target , boost : : filesystem : : copy_option : : overwrite_if_exists , ec ) ;
if ( ec ) {
2020-12-01 11:01:01 +00:00
error_message = ec . message ( ) ;
2020-02-27 09:44:25 +00:00
return FAIL_COPY_FILE ;
2019-12-19 14:36:00 +00:00
}
2020-11-04 08:23:47 +00:00
ec . clear ( ) ;
2020-10-23 13:36:14 +00:00
boost : : filesystem : : permissions ( target , perms , ec ) ;
2020-11-04 08:23:47 +00:00
if ( ec )
2020-11-26 16:15:26 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " boost::filesystem::permisions after copy error message (this could be irrelevant message based on file system): " < < ec . message ( ) ;
2020-02-27 09:44:25 +00:00
return SUCCESS ;
2019-12-19 14:36:00 +00:00
}
2020-10-23 16:34:19 +00:00
CopyFileResult copy_file ( const std : : string & from , const std : : string & to , std : : string & error_message , const bool with_check )
2018-05-03 19:45:43 +00:00
{
2019-12-19 14:36:00 +00:00
std : : string to_temp = to + " .tmp " ;
2020-10-23 16:34:19 +00:00
CopyFileResult ret_val = copy_file_inner ( from , to_temp , error_message ) ;
2020-02-27 09:44:25 +00:00
if ( ret_val = = SUCCESS )
2019-12-19 14:36:00 +00:00
{
2019-12-21 11:31:32 +00:00
if ( with_check )
ret_val = check_copy ( from , to_temp ) ;
2019-12-22 10:11:48 +00:00
if ( ret_val = = 0 & & rename_file ( to_temp , to ) )
2020-02-27 09:44:25 +00:00
ret_val = FAIL_RENAMING ;
2019-12-19 14:36:00 +00:00
}
return ret_val ;
2019-12-18 12:13:40 +00:00
}
2020-02-27 09:44:25 +00:00
CopyFileResult check_copy ( const std : : string & origin , const std : : string & copy )
2019-12-18 12:13:40 +00:00
{
2020-02-19 14:59:40 +00:00
boost : : nowide : : ifstream f1 ( origin , std : : ifstream : : in | std : : ifstream : : binary | std : : ifstream : : ate ) ;
boost : : nowide : : ifstream f2 ( copy , std : : ifstream : : in | std : : ifstream : : binary | std : : ifstream : : ate ) ;
2019-12-18 12:13:40 +00:00
2020-02-19 14:59:40 +00:00
if ( f1 . fail ( ) )
2020-02-27 09:44:25 +00:00
return FAIL_CHECK_ORIGIN_NOT_OPENED ;
2020-02-19 14:59:40 +00:00
if ( f2 . fail ( ) )
2020-02-27 09:44:25 +00:00
return FAIL_CHECK_TARGET_NOT_OPENED ;
2019-12-18 12:13:40 +00:00
2019-12-22 10:11:48 +00:00
std : : streampos fsize = f1 . tellg ( ) ;
if ( fsize ! = f2 . tellg ( ) )
2020-02-27 09:44:25 +00:00
return FAIL_FILES_DIFFERENT ;
2019-11-01 09:51:26 +00:00
2019-12-18 12:13:40 +00:00
f1 . seekg ( 0 , std : : ifstream : : beg ) ;
f2 . seekg ( 0 , std : : ifstream : : beg ) ;
2019-12-22 10:11:48 +00:00
// Compare by reading 8 MiB buffers one at a time.
size_t buffer_size = 8 * 1024 * 1024 ;
std : : vector < char > buffer_origin ( buffer_size , 0 ) ;
std : : vector < char > buffer_copy ( buffer_size , 0 ) ;
do {
f1 . read ( buffer_origin . data ( ) , buffer_size ) ;
f2 . read ( buffer_copy . data ( ) , buffer_size ) ;
std : : streampos origin_cnt = f1 . gcount ( ) ;
std : : streampos copy_cnt = f2 . gcount ( ) ;
if ( origin_cnt ! = copy_cnt | |
( origin_cnt > 0 & & std : : memcmp ( buffer_origin . data ( ) , buffer_copy . data ( ) , origin_cnt ) ! = 0 ) )
// Files are different.
2020-02-27 09:44:25 +00:00
return FAIL_FILES_DIFFERENT ;
2019-12-22 10:11:48 +00:00
fsize - = origin_cnt ;
} while ( f1 . good ( ) & & f2 . good ( ) ) ;
// All data has been read and compared equal.
2020-02-27 09:44:25 +00:00
return ( f1 . eof ( ) & & f2 . eof ( ) & & fsize = = 0 ) ? SUCCESS : FAIL_FILES_DIFFERENT ;
2018-06-07 09:18:28 +00:00
}
2018-06-08 07:40:00 +00:00
2019-02-03 14:30:37 +00:00
// Ignore system and hidden files, which may be created by the DropBox synchronisation process.
2019-05-14 17:46:01 +00:00
// https://github.com/prusa3d/PrusaSlicer/issues/1298
2019-02-03 14:30:37 +00:00
bool is_plain_file ( const boost : : filesystem : : directory_entry & dir_entry )
{
if ( ! boost : : filesystem : : is_regular_file ( dir_entry . status ( ) ) )
return false ;
# ifdef _MSC_VER
DWORD attributes = GetFileAttributesW ( boost : : nowide : : widen ( dir_entry . path ( ) . string ( ) ) . c_str ( ) ) ;
return ( attributes & ( FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM ) ) = = 0 ;
# else
return true ;
# endif
}
bool is_ini_file ( const boost : : filesystem : : directory_entry & dir_entry )
{
return is_plain_file ( dir_entry ) & & strcasecmp ( dir_entry . path ( ) . extension ( ) . string ( ) . c_str ( ) , " .ini " ) = = 0 ;
}
bool is_idx_file ( const boost : : filesystem : : directory_entry & dir_entry )
{
return is_plain_file ( dir_entry ) & & strcasecmp ( dir_entry . path ( ) . extension ( ) . string ( ) . c_str ( ) , " .idx " ) = = 0 ;
}
2016-12-12 18:13:33 +00:00
} // namespace Slic3r
2018-06-07 09:18:28 +00:00
2017-08-03 17:49:41 +00:00
# ifdef WIN32
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <windows.h>
# endif /* WIN32 */
namespace Slic3r {
2017-12-14 12:47:22 +00:00
// Encode an UTF-8 string to the local code page.
2017-08-03 17:49:41 +00:00
std : : string encode_path ( const char * src )
{
# ifdef WIN32
// Convert the source utf8 encoded string to a wide string.
std : : wstring wstr_src = boost : : nowide : : widen ( src ) ;
if ( wstr_src . length ( ) = = 0 )
return std : : string ( ) ;
// Convert a wide string to a local code page.
int size_needed = : : WideCharToMultiByte ( 0 , 0 , wstr_src . data ( ) , ( int ) wstr_src . size ( ) , nullptr , 0 , nullptr , nullptr ) ;
std : : string str_dst ( size_needed , 0 ) ;
2020-01-03 15:32:56 +00:00
: : WideCharToMultiByte ( 0 , 0 , wstr_src . data ( ) , ( int ) wstr_src . size ( ) , str_dst . data ( ) , size_needed , nullptr , nullptr ) ;
2017-08-03 17:49:41 +00:00
return str_dst ;
# else /* WIN32 */
return src ;
# endif /* WIN32 */
}
2017-12-14 12:47:22 +00:00
// Encode an 8-bit string from a local code page to UTF-8.
2020-12-01 10:59:03 +00:00
// Multibyte to utf8
2017-08-03 17:49:41 +00:00
std : : string decode_path ( const char * src )
{
# ifdef WIN32
int len = int ( strlen ( src ) ) ;
if ( len = = 0 )
return std : : string ( ) ;
// Convert the string encoded using the local code page to a wide string.
int size_needed = : : MultiByteToWideChar ( 0 , 0 , src , len , nullptr , 0 ) ;
std : : wstring wstr_dst ( size_needed , 0 ) ;
2020-01-03 15:32:56 +00:00
: : MultiByteToWideChar ( 0 , 0 , src , len , wstr_dst . data ( ) , size_needed ) ;
2017-08-03 17:49:41 +00:00
// Convert a wide string to utf8.
return boost : : nowide : : narrow ( wstr_dst . c_str ( ) ) ;
# else /* WIN32 */
return src ;
# endif /* WIN32 */
}
std : : string normalize_utf8_nfc ( const char * src )
{
2017-11-02 15:21:34 +00:00
static std : : locale locale_utf8 ( boost : : locale : : generator ( ) . generate ( " " ) ) ;
2017-08-03 17:49:41 +00:00
return boost : : locale : : normalize ( src , boost : : locale : : norm_nfc , locale_utf8 ) ;
}
2017-12-21 15:56:33 +00:00
namespace PerlUtils {
// Get a file name including the extension.
std : : string path_to_filename ( const char * src ) { return boost : : filesystem : : path ( src ) . filename ( ) . string ( ) ; }
// Get a file name without the extension.
std : : string path_to_stem ( const char * src ) { return boost : : filesystem : : path ( src ) . stem ( ) . string ( ) ; }
// Get just the extension.
std : : string path_to_extension ( const char * src ) { return boost : : filesystem : : path ( src ) . extension ( ) . string ( ) ; }
// Get a directory without the trailing slash.
std : : string path_to_parent_path ( const char * src ) { return boost : : filesystem : : path ( src ) . parent_path ( ) . string ( ) ; }
} ;
2020-02-03 10:18:33 +00:00
std : : string string_printf ( const char * format , . . . )
{
va_list args1 ;
va_start ( args1 , format ) ;
va_list args2 ;
va_copy ( args2 , args1 ) ;
2020-02-03 16:11:24 +00:00
static const size_t INITIAL_LEN = 200 ;
std : : string buffer ( INITIAL_LEN , ' \0 ' ) ;
2020-02-03 10:18:33 +00:00
int bufflen = : : vsnprintf ( buffer . data ( ) , INITIAL_LEN - 1 , format , args1 ) ;
if ( bufflen > = int ( INITIAL_LEN ) ) {
buffer . resize ( size_t ( bufflen ) + 1 ) ;
: : vsnprintf ( buffer . data ( ) , buffer . size ( ) , format , args2 ) ;
}
2020-02-03 16:11:24 +00:00
buffer . resize ( bufflen ) ;
return buffer ;
2020-02-03 10:18:33 +00:00
}
2019-09-11 10:13:59 +00:00
std : : string header_slic3r_generated ( )
2017-10-30 17:15:41 +00:00
{
2020-10-05 13:42:35 +00:00
return std : : string ( " generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils : : utc_timestamp ( ) ;
2017-10-30 17:15:41 +00:00
}
2020-10-05 13:42:35 +00:00
std : : string header_gcodeviewer_generated ( )
{
return std : : string ( " generated by " GCODEVIEWER_APP_NAME " " SLIC3R_VERSION " on " ) + Utils : : utc_timestamp ( ) ;
}
2018-04-20 09:05:00 +00:00
unsigned get_current_pid ( )
{
# ifdef WIN32
return GetCurrentProcessId ( ) ;
# else
return : : getpid ( ) ;
# endif
}
2018-07-23 12:39:50 +00:00
std : : string xml_escape ( std : : string text )
{
std : : string : : size_type pos = 0 ;
for ( ; ; )
{
pos = text . find_first_of ( " \" \' &<> " , pos ) ;
if ( pos = = std : : string : : npos )
break ;
std : : string replacement ;
switch ( text [ pos ] )
{
case ' \" ' : replacement = " " " ; break ;
case ' \' ' : replacement = " ' " ; break ;
case ' & ' : replacement = " & " ; break ;
case ' < ' : replacement = " < " ; break ;
case ' > ' : replacement = " > " ; break ;
default : break ;
}
text . replace ( pos , 1 , replacement ) ;
pos + = replacement . size ( ) ;
}
return text ;
}
2018-12-18 15:32:11 +00:00
std : : string format_memsize_MB ( size_t n )
{
std : : string out ;
size_t n2 = 0 ;
size_t scale = 1 ;
// Round to MB
n + = 500000 ;
n / = 1000000 ;
while ( n > = 1000 ) {
n2 = n2 + scale * ( n % 1000 ) ;
n / = 1000 ;
scale * = 1000 ;
}
char buf [ 8 ] ;
2019-06-25 11:06:04 +00:00
sprintf ( buf , " %d " , ( int ) n ) ;
2018-12-18 15:32:11 +00:00
out = buf ;
while ( scale ! = 1 ) {
scale / = 1000 ;
n = n2 / scale ;
n2 = n2 % scale ;
2019-06-25 11:06:04 +00:00
sprintf ( buf , " ,%03d " , ( int ) n ) ;
2018-12-18 15:32:11 +00:00
out + = buf ;
}
return out + " MB " ;
}
2019-08-05 17:57:57 +00:00
// Returns platform-specific string to be used as log output or parsed in SysInfoDialog.
// The latter parses the string with (semi)colons as separators, it should look about as
// "desc1: value1; desc2: value2" or similar (spaces should not matter).
std : : string log_memory_info ( bool ignore_loglevel )
2018-12-18 10:31:41 +00:00
{
std : : string out ;
2019-08-05 17:57:57 +00:00
if ( ignore_loglevel | | logSeverity < = boost : : log : : trivial : : info ) {
# ifdef WIN32
# ifndef PROCESS_MEMORY_COUNTERS_EX
// MingW32 doesn't have this struct in psapi.h
typedef struct _PROCESS_MEMORY_COUNTERS_EX {
DWORD cb ;
DWORD PageFaultCount ;
SIZE_T PeakWorkingSetSize ;
SIZE_T WorkingSetSize ;
SIZE_T QuotaPeakPagedPoolUsage ;
SIZE_T QuotaPagedPoolUsage ;
SIZE_T QuotaPeakNonPagedPoolUsage ;
SIZE_T QuotaNonPagedPoolUsage ;
SIZE_T PagefileUsage ;
SIZE_T PeakPagefileUsage ;
SIZE_T PrivateUsage ;
} PROCESS_MEMORY_COUNTERS_EX , * PPROCESS_MEMORY_COUNTERS_EX ;
# endif /* PROCESS_MEMORY_COUNTERS_EX */
2018-12-18 10:31:41 +00:00
HANDLE hProcess = : : OpenProcess ( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ , FALSE , : : GetCurrentProcessId ( ) ) ;
if ( hProcess ! = nullptr ) {
PROCESS_MEMORY_COUNTERS_EX pmc ;
if ( GetProcessMemoryInfo ( hProcess , ( PROCESS_MEMORY_COUNTERS * ) & pmc , sizeof ( pmc ) ) )
2019-08-05 17:57:57 +00:00
out = " WorkingSet: " + format_memsize_MB ( pmc . WorkingSetSize ) + " ; PrivateBytes: " + format_memsize_MB ( pmc . PrivateUsage ) + " ; Pagefile(peak): " + format_memsize_MB ( pmc . PagefileUsage ) + " ( " + format_memsize_MB ( pmc . PeakPagefileUsage ) + " ) " ;
else
out + = " Used memory: N/A " ;
2018-12-18 10:31:41 +00:00
CloseHandle ( hProcess ) ;
}
2019-08-05 11:01:23 +00:00
# elif defined(__linux__) or defined(__APPLE__)
2019-08-05 17:57:57 +00:00
// Get current memory usage.
# ifdef __APPLE__
struct mach_task_basic_info info ;
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT ;
out + = " Resident memory: " ;
if ( task_info ( mach_task_self ( ) , MACH_TASK_BASIC_INFO , ( task_info_t ) & info , & infoCount ) = = KERN_SUCCESS )
out + = format_memsize_MB ( ( size_t ) info . resident_size ) ;
else
out + = " N/A " ;
# else // i.e. __linux__
size_t tSize = 0 , resident = 0 , share = 0 ;
std : : ifstream buffer ( " /proc/self/statm " ) ;
if ( buffer & & ( buffer > > tSize > > resident > > share ) ) {
2019-08-05 11:01:23 +00:00
size_t page_size = ( size_t ) sysconf ( _SC_PAGE_SIZE ) ; // in case x86-64 is configured to use 2MB pages
size_t rss = resident * page_size ;
2019-08-05 17:57:57 +00:00
out + = " Resident memory: " + format_memsize_MB ( rss ) ;
out + = " ; Shared memory: " + format_memsize_MB ( share * page_size ) ;
out + = " ; Private memory: " + format_memsize_MB ( rss - share * page_size ) ;
2019-08-05 11:01:23 +00:00
}
2019-08-05 17:57:57 +00:00
else
out + = " Used memory: N/A " ;
# endif
// Now get peak memory usage.
out + = " ; Peak memory usage: " ;
rusage memory_info ;
if ( getrusage ( RUSAGE_SELF , & memory_info ) = = 0 )
{
size_t peak_mem_usage = ( size_t ) memory_info . ru_maxrss ;
# ifdef __linux__
peak_mem_usage * = 1024 ; // getrusage returns the value in kB on linux
# endif
out + = format_memsize_MB ( peak_mem_usage ) ;
}
else
out + = " N/A " ;
2019-08-05 11:01:23 +00:00
# endif
}
return out ;
}
2018-12-18 10:31:41 +00:00
2019-07-17 13:48:53 +00:00
// Returns the size of physical memory (RAM) in bytes.
// http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
size_t total_physical_memory ( )
{
# if defined(_WIN32) && (defined(__CYGWIN__) || defined(__CYGWIN32__))
// Cygwin under Windows. ------------------------------------
// New 64-bit MEMORYSTATUSEX isn't available. Use old 32.bit
MEMORYSTATUS status ;
status . dwLength = sizeof ( status ) ;
GlobalMemoryStatus ( & status ) ;
return ( size_t ) status . dwTotalPhys ;
# elif defined(_WIN32)
// Windows. -------------------------------------------------
// Use new 64-bit MEMORYSTATUSEX, not old 32-bit MEMORYSTATUS
MEMORYSTATUSEX status ;
status . dwLength = sizeof ( status ) ;
GlobalMemoryStatusEx ( & status ) ;
return ( size_t ) status . ullTotalPhys ;
# elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
// UNIX variants. -------------------------------------------
// Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM
# if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
int mib [ 2 ] ;
mib [ 0 ] = CTL_HW ;
# if defined(HW_MEMSIZE)
mib [ 1 ] = HW_MEMSIZE ; // OSX. ---------------------
# elif defined(HW_PHYSMEM64)
mib [ 1 ] = HW_PHYSMEM64 ; // NetBSD, OpenBSD. ---------
# endif
int64_t size = 0 ; // 64-bit
size_t len = sizeof ( size ) ;
if ( sysctl ( mib , 2 , & size , & len , NULL , 0 ) = = 0 )
return ( size_t ) size ;
return 0L ; // Failed?
# elif defined(_SC_AIX_REALMEM)
// AIX. -----------------------------------------------------
return ( size_t ) sysconf ( _SC_AIX_REALMEM ) * ( size_t ) 1024L ;
# elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
// FreeBSD, Linux, OpenBSD, and Solaris. --------------------
return ( size_t ) sysconf ( _SC_PHYS_PAGES ) *
( size_t ) sysconf ( _SC_PAGESIZE ) ;
# elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE)
// Legacy. --------------------------------------------------
return ( size_t ) sysconf ( _SC_PHYS_PAGES ) *
( size_t ) sysconf ( _SC_PAGE_SIZE ) ;
# elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
// DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. --------
int mib [ 2 ] ;
mib [ 0 ] = CTL_HW ;
# if defined(HW_REALMEM)
mib [ 1 ] = HW_REALMEM ; // FreeBSD. -----------------
# elif defined(HW_PYSMEM)
mib [ 1 ] = HW_PHYSMEM ; // Others. ------------------
# endif
unsigned int size = 0 ; // 32-bit
size_t len = sizeof ( size ) ;
if ( sysctl ( mib , 2 , & size , & len , NULL , 0 ) = = 0 )
return ( size_t ) size ;
return 0L ; // Failed?
# endif // sysctl and sysconf variants
# else
return 0L ; // Unknown OS.
# endif
}
2017-08-03 17:49:41 +00:00
} ; // namespace Slic3r