2020-07-03 09:40:31 -04:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2025-01-01 13:30:11 -08:00
|
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
2020-07-03 09:40:31 -04:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2023-04-12 17:14:45 +01:00
|
|
|
#include <memory>
|
2020-07-03 09:40:31 -04:00
|
|
|
#include <wx/dir.h>
|
|
|
|
#include <wx/filedlg.h>
|
|
|
|
#include <wx/fs_zip.h>
|
|
|
|
#include <wx/uri.h>
|
|
|
|
#include <wx/wfstream.h>
|
|
|
|
#include <wx/zipstrm.h>
|
|
|
|
|
2020-11-17 20:21:04 -05:00
|
|
|
#include <core/arraydim.h>
|
2020-07-03 09:40:31 -04:00
|
|
|
#include <macros.h>
|
|
|
|
#include <project/project_archiver.h>
|
|
|
|
#include <reporter.h>
|
|
|
|
#include <wildcards_and_files_ext.h>
|
2020-10-26 03:54:36 -07:00
|
|
|
#include <wxstream_helper.h>
|
2021-05-01 09:50:29 +02:00
|
|
|
#include <wx/log.h>
|
2024-12-29 19:53:33 -05:00
|
|
|
#include <kiplatform/io.h>
|
2020-07-03 09:40:31 -04:00
|
|
|
|
2025-01-05 20:56:18 +00:00
|
|
|
#include <regex>
|
2022-09-25 12:51:20 +01:00
|
|
|
#include <set>
|
2020-07-03 09:40:31 -04:00
|
|
|
|
|
|
|
|
2022-09-25 12:51:20 +01:00
|
|
|
#define ZipFileExtension wxT( "zip" )
|
2020-07-03 09:40:31 -04:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
class PROJECT_ARCHIVER_DIR_ZIP_TRAVERSER : public wxDirTraverser
|
2025-01-05 20:56:18 +00:00
|
|
|
{
|
|
|
|
public:
|
2025-01-13 13:05:29 -05:00
|
|
|
PROJECT_ARCHIVER_DIR_ZIP_TRAVERSER( const std::string& aExtRegex, const wxString& aPrjDir,
|
|
|
|
wxZipOutputStream& aZipFileOutput,
|
2025-01-06 02:05:45 +00:00
|
|
|
REPORTER& aReporter, bool aVerbose ) :
|
|
|
|
m_zipFile( aZipFileOutput ),
|
|
|
|
m_prjDir( aPrjDir ),
|
|
|
|
m_fileExtRegex( aExtRegex, std::regex_constants::ECMAScript | std::regex_constants::icase ),
|
|
|
|
m_reporter( aReporter ),
|
|
|
|
m_errorOccurred( false ),
|
|
|
|
m_verbose( aVerbose )
|
2025-01-05 20:56:18 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
virtual wxDirTraverseResult OnFile( const wxString& aFilename ) override
|
|
|
|
{
|
|
|
|
if( std::regex_search( aFilename.ToStdString(), m_fileExtRegex ) )
|
2025-01-06 02:05:45 +00:00
|
|
|
{
|
|
|
|
addFileToZip( aFilename );
|
|
|
|
|
|
|
|
// Special processing for IBIS files to include the corresponding pkg file
|
|
|
|
if( aFilename.EndsWith( FILEEXT::IbisFileExtension ) )
|
|
|
|
{
|
|
|
|
wxFileName package( aFilename );
|
|
|
|
package.MakeRelativeTo( m_prjDir );
|
|
|
|
package.SetExt( wxS( "pkg" ) );
|
|
|
|
KIPLATFORM::IO::LongPathAdjustment( package );
|
|
|
|
|
|
|
|
if( package.Exists() )
|
|
|
|
addFileToZip( package.GetFullPath() );
|
|
|
|
}
|
|
|
|
}
|
2025-01-05 20:56:18 +00:00
|
|
|
|
|
|
|
return wxDIR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual wxDirTraverseResult OnDir( const wxString& aDirname ) override
|
|
|
|
{
|
|
|
|
return wxDIR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
unsigned long GetUncompressedBytes() const
|
|
|
|
{
|
|
|
|
return m_uncompressedBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetErrorOccurred() const
|
|
|
|
{
|
|
|
|
return m_errorOccurred;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2025-01-13 13:05:29 -05:00
|
|
|
void addFileToZip( const wxString& aFilename )
|
2025-01-06 02:05:45 +00:00
|
|
|
{
|
|
|
|
wxString msg;
|
|
|
|
wxFileSystem fsfile;
|
|
|
|
|
|
|
|
wxFileName curr_fn( aFilename );
|
2025-01-10 13:28:16 +01:00
|
|
|
wxFileName curr_prjdir;
|
|
|
|
curr_prjdir.AssignDir( m_prjDir );
|
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
KIPLATFORM::IO::LongPathAdjustment( curr_fn );
|
2025-01-10 13:28:16 +01:00
|
|
|
KIPLATFORM::IO::LongPathAdjustment( curr_prjdir );
|
|
|
|
|
|
|
|
// Note: MakeRelativeTo() works only if curr_fn and curr_prjdir use the same
|
2025-01-13 13:05:29 -05:00
|
|
|
// long path adjustment (no long path of both use long path)
|
2025-01-10 13:28:16 +01:00
|
|
|
curr_fn.MakeRelativeTo( curr_prjdir.GetFullPath() );
|
2025-01-06 02:05:45 +00:00
|
|
|
|
|
|
|
wxString currFilename = curr_fn.GetFullPath();
|
|
|
|
|
|
|
|
// Read input file and add it to the zip file:
|
|
|
|
wxFSFile* infile = fsfile.OpenFile( currFilename );
|
|
|
|
|
|
|
|
if( infile )
|
|
|
|
{
|
|
|
|
m_zipFile.PutNextEntry( currFilename, infile->GetModificationTime() );
|
|
|
|
infile->GetStream()->Read( m_zipFile );
|
|
|
|
m_zipFile.CloseEntry();
|
|
|
|
|
|
|
|
m_uncompressedBytes += infile->GetStream()->GetSize();
|
|
|
|
|
|
|
|
if( m_verbose )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Archived file '%s'." ), currFilename );
|
|
|
|
m_reporter.Report( msg, RPT_SEVERITY_INFO );
|
|
|
|
}
|
|
|
|
|
|
|
|
delete infile;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_verbose )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Failed to archive file '%s'." ), currFilename );
|
|
|
|
m_reporter.Report( msg, RPT_SEVERITY_ERROR );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_errorOccurred = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-05 20:56:18 +00:00
|
|
|
private:
|
2025-01-06 02:05:45 +00:00
|
|
|
wxZipOutputStream& m_zipFile;
|
|
|
|
|
|
|
|
wxString m_prjDir;
|
2025-01-05 20:56:18 +00:00
|
|
|
std::regex m_fileExtRegex;
|
2025-01-06 02:05:45 +00:00
|
|
|
REPORTER& m_reporter;
|
|
|
|
|
|
|
|
bool m_errorOccurred; // True if an error archiving the file
|
|
|
|
bool m_verbose; // True to enable verbose logging
|
|
|
|
|
|
|
|
// Keep track of how many bytes would have been used without compression
|
|
|
|
unsigned long m_uncompressedBytes = 0;
|
2025-01-05 20:56:18 +00:00
|
|
|
};
|
|
|
|
|
2025-01-13 13:05:29 -05:00
|
|
|
|
2020-07-03 09:40:31 -04:00
|
|
|
PROJECT_ARCHIVER::PROJECT_ARCHIVER()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2025-01-13 13:05:29 -05:00
|
|
|
|
2022-09-25 12:51:20 +01:00
|
|
|
bool PROJECT_ARCHIVER::AreZipArchivesIdentical( const wxString& aZipFileA,
|
|
|
|
const wxString& aZipFileB, REPORTER& aReporter )
|
|
|
|
{
|
|
|
|
wxFFileInputStream streamA( aZipFileA );
|
|
|
|
wxFFileInputStream streamB( aZipFileB );
|
|
|
|
|
|
|
|
if( !streamA.IsOk() || !streamB.IsOk() )
|
|
|
|
{
|
|
|
|
aReporter.Report( _( "Could not open archive file." ), RPT_SEVERITY_ERROR );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipInputStream zipStreamA = wxZipInputStream( streamA );
|
|
|
|
wxZipInputStream zipStreamB = wxZipInputStream( streamB );
|
|
|
|
|
|
|
|
std::set<wxUint32> crcsA;
|
|
|
|
std::set<wxUint32> crcsB;
|
|
|
|
|
|
|
|
|
|
|
|
for( wxZipEntry* entry = zipStreamA.GetNextEntry(); entry; entry = zipStreamA.GetNextEntry() )
|
|
|
|
{
|
|
|
|
crcsA.insert( entry->GetCrc() );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( wxZipEntry* entry = zipStreamB.GetNextEntry(); entry; entry = zipStreamB.GetNextEntry() )
|
|
|
|
{
|
|
|
|
crcsB.insert( entry->GetCrc() );
|
|
|
|
}
|
|
|
|
|
|
|
|
return crcsA == crcsB;
|
|
|
|
}
|
|
|
|
|
2020-07-03 09:40:31 -04:00
|
|
|
|
|
|
|
// Unarchive Files code comes from wxWidgets sample/archive/archive.cpp
|
|
|
|
bool PROJECT_ARCHIVER::Unarchive( const wxString& aSrcFile, const wxString& aDestDir,
|
|
|
|
REPORTER& aReporter )
|
|
|
|
{
|
2020-12-12 18:51:35 -05:00
|
|
|
wxFFileInputStream stream( aSrcFile );
|
2020-07-03 09:40:31 -04:00
|
|
|
|
2020-11-10 20:31:52 +00:00
|
|
|
if( !stream.IsOk() )
|
2020-07-03 09:40:31 -04:00
|
|
|
{
|
2021-07-05 13:40:38 +01:00
|
|
|
aReporter.Report( _( "Could not open archive file." ), RPT_SEVERITY_ERROR );
|
2020-07-03 09:40:31 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const wxArchiveClassFactory* archiveClassFactory =
|
|
|
|
wxArchiveClassFactory::Find( aSrcFile, wxSTREAM_FILEEXT );
|
|
|
|
|
|
|
|
if( !archiveClassFactory )
|
|
|
|
{
|
2021-07-05 13:40:38 +01:00
|
|
|
aReporter.Report( _( "Invalid archive file format." ), RPT_SEVERITY_ERROR );
|
2020-07-03 09:40:31 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-12 17:14:45 +01:00
|
|
|
std::unique_ptr<wxArchiveInputStream> archiveStream( archiveClassFactory->NewStream( stream ) );
|
2020-07-03 09:40:31 -04:00
|
|
|
|
|
|
|
wxString fileStatus;
|
|
|
|
|
|
|
|
for( wxArchiveEntry* entry = archiveStream->GetNextEntry(); entry;
|
|
|
|
entry = archiveStream->GetNextEntry() )
|
|
|
|
{
|
2021-07-05 13:40:38 +01:00
|
|
|
fileStatus.Printf( _( "Extracting file '%s'." ), entry->GetName() );
|
2020-07-03 09:40:31 -04:00
|
|
|
aReporter.Report( fileStatus, RPT_SEVERITY_INFO );
|
|
|
|
|
|
|
|
wxString fullname = aDestDir + entry->GetName();
|
|
|
|
|
2020-10-26 03:54:36 -07:00
|
|
|
// Ensure the target directory exists and create it if not
|
2020-07-03 09:40:31 -04:00
|
|
|
wxString t_path = wxPathOnly( fullname );
|
|
|
|
|
|
|
|
if( !wxDirExists( t_path ) )
|
|
|
|
{
|
2020-10-26 03:54:36 -07:00
|
|
|
wxFileName::Mkdir( t_path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
|
2020-07-03 09:40:31 -04:00
|
|
|
}
|
|
|
|
|
2021-05-28 15:41:21 -07:00
|
|
|
// Directory entries need only be created, not extracted (0 size)
|
|
|
|
if( entry->IsDir() )
|
|
|
|
continue;
|
|
|
|
|
2021-09-01 01:04:43 -04:00
|
|
|
|
2020-07-03 09:40:31 -04:00
|
|
|
wxTempFileOutputStream outputFileStream( fullname );
|
|
|
|
|
|
|
|
if( CopyStreamData( *archiveStream, outputFileStream, entry->GetSize() ) )
|
|
|
|
outputFileStream.Commit();
|
|
|
|
else
|
2021-07-05 13:40:38 +01:00
|
|
|
aReporter.Report( _( "Error extracting file!" ), RPT_SEVERITY_ERROR );
|
2021-09-01 01:04:43 -04:00
|
|
|
|
|
|
|
// Now let's set the filetimes based on what's in the zip
|
|
|
|
wxFileName outputFileName( fullname );
|
|
|
|
wxDateTime fileTime = entry->GetDateTime();
|
2025-01-13 13:05:29 -05:00
|
|
|
|
2021-09-01 01:04:43 -04:00
|
|
|
// For now we set access, mod, create to the same datetime
|
|
|
|
// create (third arg) is only used on Windows
|
|
|
|
outputFileName.SetTimes( &fileTime, &fileTime, &fileTime );
|
2020-07-03 09:40:31 -04:00
|
|
|
}
|
|
|
|
|
2021-07-05 13:40:38 +01:00
|
|
|
aReporter.Report( wxT( "Extracted project." ), RPT_SEVERITY_INFO );
|
2020-07-03 09:40:31 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PROJECT_ARCHIVER::Archive( const wxString& aSrcDir, const wxString& aDestFile,
|
2021-02-04 17:24:02 -05:00
|
|
|
REPORTER& aReporter, bool aVerbose, bool aIncludeExtraFiles )
|
2020-07-03 09:40:31 -04:00
|
|
|
{
|
2025-01-05 20:56:18 +00:00
|
|
|
|
|
|
|
#define EXT( ext ) "\\." + ext + "|"
|
2025-01-05 21:59:01 +00:00
|
|
|
#define NAME( name ) name + "|"
|
|
|
|
#define EXT_NO_PIPE( ext ) "\\." + ext
|
2025-01-05 20:56:18 +00:00
|
|
|
#define NAME_NO_PIPE( name ) name
|
|
|
|
|
2021-02-04 17:24:02 -05:00
|
|
|
// List of file extensions that are always archived
|
2025-01-05 20:56:18 +00:00
|
|
|
std::string fileExtensionRegex = "("
|
|
|
|
EXT( FILEEXT::ProjectFileExtension )
|
|
|
|
EXT( FILEEXT::ProjectLocalSettingsFileExtension )
|
|
|
|
EXT( FILEEXT::KiCadSchematicFileExtension )
|
|
|
|
EXT( FILEEXT::KiCadSymbolLibFileExtension )
|
|
|
|
EXT( FILEEXT::KiCadPcbFileExtension )
|
|
|
|
EXT( FILEEXT::KiCadFootprintFileExtension )
|
|
|
|
EXT( FILEEXT::DesignRulesFileExtension )
|
|
|
|
EXT( FILEEXT::DrawingSheetFileExtension )
|
|
|
|
EXT( FILEEXT::KiCadJobSetFileExtension )
|
|
|
|
EXT( FILEEXT::JsonFileExtension ) // for design blocks
|
2025-01-05 21:59:01 +00:00
|
|
|
EXT( FILEEXT::WorkbookFileExtension ) +
|
|
|
|
NAME( FILEEXT::FootprintLibraryTableFileName ) +
|
|
|
|
NAME( FILEEXT::SymbolLibraryTableFileName ) +
|
|
|
|
NAME_NO_PIPE( FILEEXT::DesignBlockLibraryTableFileName );
|
2021-02-04 17:24:02 -05:00
|
|
|
|
|
|
|
// List of additional file extensions that are only archived when aIncludeExtraFiles is true
|
2025-01-05 20:56:18 +00:00
|
|
|
if( aIncludeExtraFiles )
|
|
|
|
{
|
|
|
|
fileExtensionRegex += "|"
|
|
|
|
EXT( FILEEXT::LegacyProjectFileExtension )
|
|
|
|
EXT( FILEEXT::LegacySchematicFileExtension )
|
|
|
|
EXT( FILEEXT::LegacySymbolLibFileExtension )
|
|
|
|
EXT( FILEEXT::LegacySymbolDocumentFileExtension )
|
|
|
|
EXT( FILEEXT::FootprintAssignmentFileExtension )
|
|
|
|
EXT( FILEEXT::LegacyPcbFileExtension )
|
|
|
|
EXT( FILEEXT::LegacyFootprintLibPathExtension )
|
|
|
|
EXT( FILEEXT::StepFileAbrvExtension ) // 3d files
|
|
|
|
EXT( FILEEXT::StepFileExtension ) // 3d files
|
|
|
|
EXT( FILEEXT::VrmlFileExtension ) // 3d files
|
|
|
|
EXT( FILEEXT::GerberFileExtensionsRegex ) // Gerber files (g?, g??, .gm12 (from protel export))
|
|
|
|
EXT( FILEEXT::GerberJobFileExtension ) // Gerber job files
|
|
|
|
EXT( FILEEXT::FootprintPlaceFileExtension ) // Our position files
|
|
|
|
EXT( FILEEXT::DrillFileExtension ) // Fab drill files
|
|
|
|
EXT( "nc" ) // Fab drill files
|
|
|
|
EXT( "xnc" ) // Fab drill files
|
|
|
|
EXT( FILEEXT::IpcD356FileExtension )
|
|
|
|
EXT( FILEEXT::ReportFileExtension )
|
2025-01-05 21:59:01 +00:00
|
|
|
EXT( FILEEXT::NetlistFileExtension )
|
|
|
|
EXT( FILEEXT::PythonFileExtension )
|
2025-01-05 20:56:18 +00:00
|
|
|
EXT( FILEEXT::PdfFileExtension )
|
|
|
|
EXT( FILEEXT::TextFileExtension )
|
|
|
|
EXT( FILEEXT::SpiceFileExtension ) // SPICE files
|
2025-01-05 21:59:01 +00:00
|
|
|
EXT( FILEEXT::SpiceSubcircuitFileExtension ) // SPICE files
|
|
|
|
EXT( FILEEXT::SpiceModelFileExtension ) // SPICE files
|
|
|
|
EXT_NO_PIPE( FILEEXT::IbisFileExtension );
|
2025-01-05 20:56:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fileExtensionRegex += ")";
|
|
|
|
|
|
|
|
#undef EXT
|
|
|
|
#undef NAME
|
|
|
|
#undef EXT_NO_PIPE
|
|
|
|
#undef NAME_NO_PIPE
|
2020-07-03 09:40:31 -04:00
|
|
|
|
|
|
|
bool success = true;
|
|
|
|
wxString msg;
|
|
|
|
wxString oldCwd = wxGetCwd();
|
|
|
|
|
2024-12-29 19:53:33 -05:00
|
|
|
wxFileName sourceDir( aSrcDir );
|
|
|
|
KIPLATFORM::IO::LongPathAdjustment( sourceDir );
|
|
|
|
wxSetWorkingDirectory( sourceDir.GetFullPath() );
|
2020-07-03 09:40:31 -04:00
|
|
|
|
|
|
|
wxFFileOutputStream ostream( aDestFile );
|
|
|
|
|
|
|
|
if( !ostream.IsOk() ) // issue to create the file. Perhaps not writable dir
|
|
|
|
{
|
2021-07-05 13:40:38 +01:00
|
|
|
msg.Printf( _( "Failed to create file '%s'." ), aDestFile );
|
2020-07-03 09:40:31 -04:00
|
|
|
aReporter.Report( msg, RPT_SEVERITY_ERROR );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipOutputStream zipstream( ostream, -1, wxConvUTF8 );
|
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
wxDir projectDir( aSrcDir );
|
|
|
|
wxString currFilename;
|
2020-07-03 09:40:31 -04:00
|
|
|
wxArrayString files;
|
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
if( !projectDir.IsOpened() )
|
2021-02-04 17:24:02 -05:00
|
|
|
{
|
2025-01-06 02:05:45 +00:00
|
|
|
if( aVerbose )
|
2025-01-05 20:56:18 +00:00
|
|
|
{
|
2025-01-06 02:05:45 +00:00
|
|
|
msg.Printf( _( "Error opening directory: '%s'." ), aSrcDir );
|
|
|
|
aReporter.Report( msg, RPT_SEVERITY_ERROR );
|
2025-01-05 20:56:18 +00:00
|
|
|
}
|
2021-02-04 17:24:02 -05:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
wxSetWorkingDirectory( oldCwd );
|
|
|
|
return false;
|
2023-02-14 11:58:12 +00:00
|
|
|
}
|
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
try
|
2020-07-03 09:40:31 -04:00
|
|
|
{
|
2025-01-06 02:05:45 +00:00
|
|
|
PROJECT_ARCHIVER_DIR_ZIP_TRAVERSER traverser( fileExtensionRegex, aSrcDir, zipstream,
|
2025-01-13 13:05:29 -05:00
|
|
|
aReporter, aVerbose );
|
2023-11-21 09:24:14 +01:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
projectDir.Traverse( traverser );
|
2023-11-21 09:24:14 +01:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
success = !traverser.GetErrorOccurred();
|
2020-07-03 09:40:31 -04:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
auto reportSize =
|
|
|
|
[]( unsigned long aSize ) -> wxString
|
|
|
|
{
|
|
|
|
constexpr float KB = 1024.0;
|
|
|
|
constexpr float MB = KB * 1024.0;
|
2024-12-29 19:53:33 -05:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
if( aSize >= MB )
|
|
|
|
return wxString::Format( wxT( "%0.2f MB" ), aSize / MB );
|
|
|
|
else if( aSize >= KB )
|
|
|
|
return wxString::Format( wxT( "%0.2f KB" ), aSize / KB );
|
|
|
|
else
|
|
|
|
return wxString::Format( wxT( "%lu bytes" ), aSize );
|
|
|
|
};
|
2020-07-03 09:40:31 -04:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
size_t zipBytesCnt = ostream.GetSize();
|
|
|
|
unsigned long uncompressedBytes = traverser.GetUncompressedBytes();
|
2020-07-03 09:40:31 -04:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
if( zipstream.Close() )
|
2020-07-03 09:40:31 -04:00
|
|
|
{
|
2025-01-06 02:05:45 +00:00
|
|
|
msg.Printf( _( "Zip archive '%s' created (%s uncompressed, %s compressed)." ),
|
|
|
|
aDestFile,
|
|
|
|
reportSize( uncompressedBytes ),
|
|
|
|
reportSize( zipBytesCnt ) );
|
|
|
|
aReporter.Report( msg, RPT_SEVERITY_INFO );
|
2020-07-03 09:40:31 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-01-06 02:05:45 +00:00
|
|
|
msg.Printf( wxT( "Failed to create file '%s'." ), aDestFile );
|
|
|
|
aReporter.Report( msg, RPT_SEVERITY_ERROR );
|
2020-07-03 09:40:31 -04:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
}
|
2025-01-06 02:05:45 +00:00
|
|
|
catch( const std::regex_error& e )
|
|
|
|
{
|
|
|
|
// Something bad happened here with the regex
|
|
|
|
wxASSERT_MSG( false, e.what() );
|
2020-07-03 09:40:31 -04:00
|
|
|
|
2025-01-06 02:05:45 +00:00
|
|
|
if( aVerbose )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Error: '%s'." ), e.what() );
|
|
|
|
aReporter.Report( msg, RPT_SEVERITY_ERROR );
|
|
|
|
}
|
2020-07-03 09:40:31 -04:00
|
|
|
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxSetWorkingDirectory( oldCwd );
|
|
|
|
return success;
|
|
|
|
}
|