diff --git a/common/dialogs/dialog_global_design_block_lib_table_config.cpp b/common/dialogs/dialog_global_design_block_lib_table_config.cpp index 6bb94b0350..8b787cc99d 100644 --- a/common/dialogs/dialog_global_design_block_lib_table_config.cpp +++ b/common/dialogs/dialog_global_design_block_lib_table_config.cpp @@ -27,6 +27,8 @@ #include "design_block_lib_table.h" +#include + DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG::DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG( wxWindow* aParent ) : @@ -124,6 +126,9 @@ bool DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG::TransferDataFromWindow() fn.GetFullPath(), designBlockTableFileName.GetFullPath() ) ); return false; } + + // Ensure the copied file is writable + KIPLATFORM::IO::MakeWriteable( designBlockTableFileName.GetFullPath() ); } // Load the successfully copied design block library table file. This should not fail since the diff --git a/eeschema/dialogs/dialog_global_sym_lib_table_config.cpp b/eeschema/dialogs/dialog_global_sym_lib_table_config.cpp index 235bc2febf..7afeda8d9d 100644 --- a/eeschema/dialogs/dialog_global_sym_lib_table_config.cpp +++ b/eeschema/dialogs/dialog_global_sym_lib_table_config.cpp @@ -25,6 +25,7 @@ #include #include +#include #include "symbol_lib_table.h" @@ -118,6 +119,9 @@ bool DIALOG_GLOBAL_SYM_LIB_TABLE_CONFIG::TransferDataFromWindow() fn.GetFullPath(), symTableFileName.GetFullPath() ) ); return false; } + + // Ensure the copied file is writable + KIPLATFORM::IO::MakeWriteable( symTableFileName.GetFullPath() ); } // Load the successfully copied symbol library table file. This should not fail since the diff --git a/libs/kiplatform/include/kiplatform/io.h b/libs/kiplatform/include/kiplatform/io.h index 0abd63a8a9..022b12bfc5 100644 --- a/libs/kiplatform/include/kiplatform/io.h +++ b/libs/kiplatform/include/kiplatform/io.h @@ -46,6 +46,14 @@ namespace IO */ bool DuplicatePermissions( const wxString& aSrc, const wxString& aDest ); + /** + * Ensures that a file has write permissions. + * This is useful after copying files that may have been read-only. + * @param aFilePath path to the file to make writeable + * @return true if the process was successful + */ + bool MakeWriteable( const wxString& aFilePath ); + /** * Helper function to determine the status of the 'Hidden' file attribute. * @return true if the file attribut is set. diff --git a/libs/kiplatform/os/apple/io.mm b/libs/kiplatform/os/apple/io.mm index bed3acf782..faf230adca 100644 --- a/libs/kiplatform/os/apple/io.mm +++ b/libs/kiplatform/os/apple/io.mm @@ -65,6 +65,44 @@ bool KIPLATFORM::IO::DuplicatePermissions(const wxString& sourceFilePath, const } } +bool KIPLATFORM::IO::MakeWriteable( const wxString& aFilePath ) +{ + NSString *path = [NSString stringWithUTF8String:aFilePath.utf8_str()]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSError *error; + NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:&error]; + + if( !attributes ) + { + NSLog(@"Error retrieving file attributes: %@", error); + return false; + } + + NSNumber *permissions = attributes[NSFilePosixPermissions]; + + if( permissions == nil ) + { + return false; + } + + // Add user write permission (S_IWUSR = 0200) + unsigned short currentPerms = [permissions unsignedShortValue]; + unsigned short newPerms = currentPerms | 0200; + + if( [fileManager setAttributes:@{NSFilePosixPermissions: @(newPerms)} + ofItemAtPath:path + error:&error] ) + { + return true; + } + else + { + NSLog(@"Error setting permissions: %@", error); + return false; + } +} + bool KIPLATFORM::IO::IsFileHidden( const wxString& aFileName ) { wxFileName fn( aFileName ); diff --git a/libs/kiplatform/os/unix/io.cpp b/libs/kiplatform/os/unix/io.cpp index dbfd956d83..82d80d107f 100644 --- a/libs/kiplatform/os/unix/io.cpp +++ b/libs/kiplatform/os/unix/io.cpp @@ -67,6 +67,21 @@ bool KIPLATFORM::IO::DuplicatePermissions( const wxString &aSrc, const wxString } } +bool KIPLATFORM::IO::MakeWriteable( const wxString& aFilePath ) +{ + struct stat fileStat; + if( stat( aFilePath.fn_str(), &fileStat ) == 0 ) + { + // Add user write permission to existing permissions + mode_t newPermissions = fileStat.st_mode | S_IWUSR; + if( chmod( aFilePath.fn_str(), newPermissions ) == 0 ) + { + return true; + } + } + return false; +} + bool KIPLATFORM::IO::IsFileHidden( const wxString& aFileName ) { wxFileName fn( aFileName ); diff --git a/libs/kiplatform/os/windows/io.cpp b/libs/kiplatform/os/windows/io.cpp index 1b8d1cca8f..50fc209909 100644 --- a/libs/kiplatform/os/windows/io.cpp +++ b/libs/kiplatform/os/windows/io.cpp @@ -115,6 +115,23 @@ bool KIPLATFORM::IO::DuplicatePermissions( const wxString &aSrc, const wxString return retval; } +bool KIPLATFORM::IO::MakeWriteable( const wxString& aFilePath ) +{ + DWORD attrs = GetFileAttributesW( aFilePath.wc_str() ); + + if( attrs == INVALID_FILE_ATTRIBUTES ) + return false; + + // Remove read-only attribute if present + if( attrs & FILE_ATTRIBUTE_READONLY ) + { + attrs &= ~FILE_ATTRIBUTE_READONLY; + return SetFileAttributesW( aFilePath.wc_str(), attrs ) != 0; + } + + return true; // Already writeable +} + bool KIPLATFORM::IO::IsFileHidden( const wxString& aFileName ) { bool result = false; diff --git a/pcbnew/dialogs/dialog_global_fp_lib_table_config.cpp b/pcbnew/dialogs/dialog_global_fp_lib_table_config.cpp index 04daa7be06..e25a6325af 100644 --- a/pcbnew/dialogs/dialog_global_fp_lib_table_config.cpp +++ b/pcbnew/dialogs/dialog_global_fp_lib_table_config.cpp @@ -26,6 +26,8 @@ #include "fp_lib_table.h" +#include + DIALOG_GLOBAL_FP_LIB_TABLE_CONFIG::DIALOG_GLOBAL_FP_LIB_TABLE_CONFIG( wxWindow* aParent ) : DIALOG_GLOBAL_LIB_TABLE_CONFIG( aParent, _( "footprint" ), KIWAY::FACE_PCB ) @@ -120,6 +122,9 @@ bool DIALOG_GLOBAL_FP_LIB_TABLE_CONFIG::TransferDataFromWindow() fn.GetFullPath(), fpTableFileName.GetFullPath() ) ); return false; } + + // Ensure the copied file is writable + KIPLATFORM::IO::MakeWriteable( fpTableFileName.GetFullPath() ); } // Load the successfully copied footprint library table file. This should not fail