mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			831 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			831 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "Utils.hpp"
 | |
| #include "I18N.hpp"
 | |
| 
 | |
| #include <locale>
 | |
| #include <ctime>
 | |
| #include <cstdarg>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #include "Time.hpp"
 | |
| 
 | |
| #ifdef WIN32
 | |
| 	#include <windows.h>
 | |
| 	#include <psapi.h>
 | |
| #else
 | |
| 	#include <unistd.h>
 | |
| 	#include <sys/types.h>
 | |
| 	#include <sys/param.h>
 | |
|     #include <sys/resource.h>
 | |
| 	#ifdef BSD
 | |
| 		#include <sys/sysctl.h>
 | |
| 	#endif
 | |
|     #ifdef __APPLE__
 | |
|         #include <mach/mach.h>
 | |
|     #endif
 | |
| #endif
 | |
| 
 | |
| #include <boost/log/core.hpp>
 | |
| #include <boost/log/trivial.hpp>
 | |
| #include <boost/log/expressions.hpp>
 | |
| 
 | |
| #include <boost/locale.hpp>
 | |
| 
 | |
| #include <boost/algorithm/string/predicate.hpp>
 | |
| #include <boost/filesystem.hpp>
 | |
| #include <boost/filesystem/path.hpp>
 | |
| #include <boost/nowide/fstream.hpp>
 | |
| #include <boost/nowide/convert.hpp>
 | |
| #include <boost/nowide/cstdio.hpp>
 | |
| 
 | |
| #include <tbb/task_scheduler_init.h>
 | |
| 
 | |
| #if defined(__linux) || defined(__GNUC__ )
 | |
| #include <strings.h>
 | |
| #endif /* __linux */
 | |
| 
 | |
| #ifdef _MSC_VER 
 | |
|     #define strcasecmp _stricmp
 | |
| #endif
 | |
| 
 | |
| namespace Slic3r {
 | |
| 
 | |
| static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
 | |
| 
 | |
| static boost::log::trivial::severity_level level_to_boost(unsigned level)
 | |
| {
 | |
|     switch (level) {
 | |
|     // Report fatal errors only.
 | |
|     case 0: return boost::log::trivial::fatal;
 | |
|     // Report fatal errors and errors.
 | |
|     case 1: return boost::log::trivial::error;
 | |
|     // Report fatal errors, errors and warnings.
 | |
|     case 2: return boost::log::trivial::warning;
 | |
|     // Report all errors, warnings and infos.
 | |
|     case 3: return boost::log::trivial::info;
 | |
|     // Report all errors, warnings, infos and debugging.
 | |
|     case 4: return boost::log::trivial::debug;
 | |
|     // Report everyting including fine level tracing information.
 | |
|     default: return boost::log::trivial::trace;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void set_logging_level(unsigned int level)
 | |
| {
 | |
|     logSeverity = level_to_boost(level);
 | |
| 
 | |
|     boost::log::core::get()->set_filter
 | |
|     (
 | |
|         boost::log::trivial::severity >= logSeverity
 | |
|     );
 | |
| }
 | |
| 
 | |
| 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;
 | |
|     case boost::log::trivial::trace : return 5;
 | |
|     default: return 1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // 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;
 | |
| 
 | |
| void trace(unsigned int level, const char *message)
 | |
| {
 | |
|     boost::log::trivial::severity_level severity = level_to_boost(level);
 | |
| 
 | |
|     BOOST_LOG_STREAM_WITH_PARAMS(::boost::log::trivial::logger::get(),\
 | |
|         (::boost::log::keywords::severity = severity)) << message;
 | |
| }
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
|     auto file = (boost::filesystem::path(g_var_dir) / file_name).make_preferred();
 | |
|     return file.string();
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| static std::string g_local_dir;
 | |
| 
 | |
| void set_local_dir(const std::string &dir)
 | |
| {
 | |
|     g_local_dir = dir;
 | |
| }
 | |
| 
 | |
| const std::string& localization_dir()
 | |
| {
 | |
| 	return g_local_dir;
 | |
| }
 | |
| 
 | |
| // Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
 | |
| Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr;
 | |
| 
 | |
| static std::string g_data_dir;
 | |
| 
 | |
| void set_data_dir(const std::string &dir)
 | |
| {
 | |
|     g_data_dir = dir;
 | |
| }
 | |
| 
 | |
| const std::string& data_dir()
 | |
| {
 | |
|     return g_data_dir;
 | |
| }
 | |
| 
 | |
| #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 */
 | |
| 
 | |
| // borrowed from LVVM lib/Support/Windows/Path.inc
 | |
| std::error_code rename_file(const std::string &from, const std::string &to)
 | |
| {
 | |
| #ifdef _WIN32
 | |
| 	return WindowsSupport::rename(from, to);
 | |
| #else
 | |
| 	boost::nowide::remove(to.c_str());
 | |
| 	return std::make_error_code(static_cast<std::errc>(boost::nowide::rename(from.c_str(), to.c_str())));
 | |
| #endif
 | |
| }
 | |
| 
 | |
| CopyFileResult copy_file_inner(const std::string& from, const std::string& to, std::string& error_message)
 | |
| {
 | |
| 	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;
 | |
| 	boost::filesystem::permissions(target, perms, ec);
 | |
| 	if (ec)
 | |
| 		BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message();
 | |
| 	ec.clear();
 | |
| 	boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
 | |
| 	if (ec) {
 | |
| 		error_message = ec.message();
 | |
| 		return FAIL_COPY_FILE;
 | |
| 	}
 | |
| 	ec.clear();
 | |
| 	boost::filesystem::permissions(target, perms, ec);
 | |
| 	if (ec)
 | |
| 		BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions after copy error message (this could be irrelevant message based on file system): " << ec.message();
 | |
| 	return SUCCESS;
 | |
| }
 | |
| 
 | |
| CopyFileResult copy_file(const std::string &from, const std::string &to, std::string& error_message, const bool with_check)
 | |
| {
 | |
| 	std::string to_temp = to + ".tmp";
 | |
| 	CopyFileResult ret_val = copy_file_inner(from, to_temp, error_message);
 | |
|     if(ret_val == SUCCESS)
 | |
| 	{
 | |
|         if (with_check)
 | |
|             ret_val = check_copy(from, to_temp);
 | |
| 
 | |
|         if (ret_val == 0 && rename_file(to_temp, to))
 | |
|         	ret_val = FAIL_RENAMING;
 | |
| 	}
 | |
| 	return ret_val;
 | |
| }
 | |
| 
 | |
| CopyFileResult check_copy(const std::string &origin, const std::string ©)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	if (f1.fail())
 | |
| 		return FAIL_CHECK_ORIGIN_NOT_OPENED;
 | |
| 	if (f2.fail())
 | |
| 		return FAIL_CHECK_TARGET_NOT_OPENED;
 | |
| 
 | |
| 	std::streampos fsize = f1.tellg();
 | |
| 	if (fsize != f2.tellg())
 | |
| 		return FAIL_FILES_DIFFERENT;
 | |
| 
 | |
| 	f1.seekg(0, std::ifstream::beg);
 | |
| 	f2.seekg(0, std::ifstream::beg);
 | |
| 
 | |
| 	// 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.
 | |
| 			return FAIL_FILES_DIFFERENT;
 | |
| 		fsize -= origin_cnt;
 | |
|     } while (f1.good() && f2.good());
 | |
| 
 | |
|     // All data has been read and compared equal.
 | |
|     return (f1.eof() && f2.eof() && fsize == 0) ? SUCCESS : FAIL_FILES_DIFFERENT;
 | |
| }
 | |
| 
 | |
| // Ignore system and hidden files, which may be created by the DropBox synchronisation process.
 | |
| // https://github.com/prusa3d/PrusaSlicer/issues/1298
 | |
| 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;
 | |
| }
 | |
| 
 | |
| } // namespace Slic3r
 | |
| 
 | |
| #ifdef WIN32
 | |
|     #ifndef NOMINMAX
 | |
|     # define NOMINMAX
 | |
|     #endif
 | |
|     #include <windows.h>
 | |
| #endif /* WIN32 */
 | |
| 
 | |
| namespace Slic3r {
 | |
| 
 | |
| // Encode an UTF-8 string to the local code page.
 | |
| 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);
 | |
|     ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), str_dst.data(), size_needed, nullptr, nullptr);
 | |
|     return str_dst;
 | |
| #else /* WIN32 */
 | |
|     return src;
 | |
| #endif /* WIN32 */
 | |
| }
 | |
| 
 | |
| // Encode an 8-bit string from a local code page to UTF-8.
 | |
| 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);
 | |
|     ::MultiByteToWideChar(0, 0, src, len, wstr_dst.data(), size_needed);
 | |
|     // 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)
 | |
| {
 | |
|     static std::locale locale_utf8(boost::locale::generator().generate(""));
 | |
|     return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8);
 | |
| }
 | |
| 
 | |
| 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(); }
 | |
| };
 | |
| 
 | |
| 
 | |
| std::string string_printf(const char *format, ...)
 | |
| {
 | |
|     va_list args1;
 | |
|     va_start(args1, format);
 | |
|     va_list args2;
 | |
|     va_copy(args2, args1);
 | |
|     
 | |
|     static const size_t INITIAL_LEN = 200;
 | |
|     std::string buffer(INITIAL_LEN, '\0');
 | |
|     
 | |
|     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);
 | |
|     }
 | |
|     
 | |
|     buffer.resize(bufflen);
 | |
|     
 | |
|     return buffer;
 | |
| }
 | |
| 
 | |
| std::string header_slic3r_generated()
 | |
| {
 | |
| 	return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp();
 | |
| }
 | |
| 
 | |
| std::string header_gcodeviewer_generated()
 | |
| {
 | |
| 	return std::string("generated by " GCODEVIEWER_APP_NAME " " SLIC3R_VERSION " on ") + Utils::utc_timestamp();
 | |
| }
 | |
| 
 | |
| unsigned get_current_pid()
 | |
| {
 | |
| #ifdef WIN32
 | |
|     return GetCurrentProcessId();
 | |
| #else
 | |
|     return ::getpid();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| 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];
 | |
|     sprintf(buf, "%d", (int)n);
 | |
|     out = buf;
 | |
|     while (scale != 1) {
 | |
|         scale /= 1000;
 | |
|         n = n2 / scale;
 | |
|         n2 = n2  % scale;
 | |
|         sprintf(buf, ",%03d", (int)n);
 | |
|         out += buf;
 | |
|     }
 | |
|     return out + "MB";
 | |
| }
 | |
| 
 | |
| // 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)
 | |
| {
 | |
|     std::string out;
 | |
|     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 */
 | |
| 
 | |
| 
 | |
|         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)))
 | |
|                 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";
 | |
|             CloseHandle(hProcess);
 | |
|         }
 | |
| #elif defined(__linux__) or defined(__APPLE__)
 | |
|         // 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)) {
 | |
|             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;
 | |
|             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);
 | |
|         }
 | |
|         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";
 | |
| #endif
 | |
|     }
 | |
|     return out;
 | |
| }
 | |
| 
 | |
| // 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
 | |
| }
 | |
| 
 | |
| }; // namespace Slic3r
 | 
