KiCad PCB EDA Suite
project_archiver.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <wx/dir.h>
21 #include <wx/filedlg.h>
22 #include <wx/fs_zip.h>
23 #include <wx/uri.h>
24 #include <wx/wfstream.h>
25 #include <wx/zipstrm.h>
26 
27 #include <macros.h>
29 #include <reporter.h>
31 
32 
33 #define ZipFileExtension wxT( "zip" )
34 
35 
37 {
38 }
39 
40 
41 // Unarchive Files code comes from wxWidgets sample/archive/archive.cpp
42 static bool CopyStreamData( wxInputStream& inputStream, wxOutputStream& outputStream,
43  wxFileOffset size )
44 {
45  wxChar buf[128 * 1024];
46  int readSize = 128 * 1024;
47  wxFileOffset copiedData = 0;
48 
49  for (;;)
50  {
51  if (size != -1 && copiedData + readSize > size)
52  readSize = size - copiedData;
53 
54  inputStream.Read( buf, readSize );
55 
56  size_t actuallyRead = inputStream.LastRead();
57  outputStream.Write( buf, actuallyRead );
58 
59  if( outputStream.LastWrite() != actuallyRead )
60  {
61  wxLogError( "Failed to output data" );
62  //return false;
63  }
64 
65  if (size == -1)
66  {
67  if (inputStream.Eof())
68  break;
69  }
70  else
71  {
72  copiedData += actuallyRead;
73  if( copiedData >= size )
74  break;
75  }
76  }
77 
78  return true;
79 }
80 
81 
82 bool PROJECT_ARCHIVER::Unarchive( const wxString& aSrcFile, const wxString& aDestDir,
83  REPORTER& aReporter )
84 {
85  wxFileInputStream stream( aSrcFile );
86 
87  if (!stream.IsOk())
88  {
89  aReporter.Report( _( "Could not open archive file\n" ), RPT_SEVERITY_ERROR );
90  return false;
91  }
92 
93  const wxArchiveClassFactory* archiveClassFactory =
94  wxArchiveClassFactory::Find( aSrcFile, wxSTREAM_FILEEXT );
95 
96  if( !archiveClassFactory )
97  {
98  aReporter.Report( _( "Invalid archive file format\n" ), RPT_SEVERITY_ERROR );
99  return false;
100  }
101 
102  wxScopedPtr<wxArchiveInputStream> archiveStream( archiveClassFactory->NewStream( stream ) );
103 
104  wxString fileStatus;
105 
106  for( wxArchiveEntry* entry = archiveStream->GetNextEntry(); entry;
107  entry = archiveStream->GetNextEntry() )
108  {
109  fileStatus.Printf( _( "Extracting file \"%s\"\n" ), entry->GetName() );
110  aReporter.Report( fileStatus, RPT_SEVERITY_INFO );
111 
112  wxString fullname = aDestDir + entry->GetName();
113 
114  // Ensure the target directory exists and created it if not
115  wxString t_path = wxPathOnly( fullname );
116 
117  if( !wxDirExists( t_path ) )
118  {
119  // To create t_path, we need to create all subdirs from unzipDir
120  // to t_path.
121  wxFileName pathToCreate;
122  pathToCreate.AssignDir( t_path );
123  pathToCreate.MakeRelativeTo( aDestDir );
124 
125  // Create the list of subdirs candidates
126  wxArrayString subDirs;
127  subDirs = pathToCreate.GetDirs();
128  pathToCreate.AssignDir( aDestDir );
129 
130  for( size_t ii = 0; ii < subDirs.Count(); ii++ )
131  {
132  pathToCreate.AppendDir( subDirs[ii] );
133  wxString currPath = pathToCreate.GetPath();
134 
135  if( !wxDirExists( currPath ) )
136  wxMkdir( currPath );
137  }
138  }
139 
140  wxTempFileOutputStream outputFileStream( fullname );
141 
142  if( CopyStreamData( *archiveStream, outputFileStream, entry->GetSize() ) )
143  outputFileStream.Commit();
144  else
145  aReporter.Report( _( "Error extracting file!\n" ), RPT_SEVERITY_ERROR );
146  }
147 
148  aReporter.Report( wxT( "Extracted project\n" ), RPT_SEVERITY_INFO );
149  return true;
150 }
151 
152 
153 bool PROJECT_ARCHIVER::Archive( const wxString& aSrcDir, const wxString& aDestFile,
154  REPORTER& aReporter, bool aVerbose )
155 {
156  // List of file extensions to save.
157  static const wxChar* extensionList[] = {
158  wxT( "*.pro" ),
159  wxT( "*.kicad_pro" ),
160  wxT( "*.kicad_prl" ),
161  wxT( "*.sch" ), // Legacy schematic files
162  wxT( "*.kicad_sch" ), // Schematic files
163  wxT( "*.lib" ), wxT( "*.dcm" ), // Legacy schematic library files
164  wxT( "*.kicad_sym" ), // schematic library files
165  wxT( "*.cmp" ),
166  wxT( "*.brd" ), wxT( "*.kicad_pcb" ), // Brd files
167  wxT( "*.mod" ), wxT( "*.kicad_mod" ), // fp files
168  wxT( "*.gb?" ), wxT( "*.gbrjob" ), // Gerber files
169  wxT( "*.gko" ), wxT( "*.gm1" ),
170  wxT( "*.gm2" ), wxT( "*.g?" ),
171  wxT( "*.gp1" ), wxT( "*.gp2" ),
172  wxT( "*.gpb" ), wxT( "*.gpt" ),
173  wxT( "*.gt?" ),
174  wxT( "*.pos" ), wxT( "*.drl" ), wxT( "*.nc" ), wxT( "*.xnc" ), // Fab files
175  wxT( "*.d356" ), wxT( "*.rpt" ),
176  wxT( "*.stp" ), wxT( "*.step" ), // 3d files
177  wxT( "*.wrl" ),
178  wxT( "*.net" ), wxT( "*.py" ),
179  wxT( "*.pdf" ), wxT( "*.txt" ), wxT( "*.kicad_wks" ),
180  wxT( "fp-lib-table" ), wxT( "sym-lib-table" )
181  };
182 
183  bool success = true;
184  wxString msg;
185  wxString oldCwd = wxGetCwd();
186 
187  wxSetWorkingDirectory( aSrcDir );
188 
189  wxFFileOutputStream ostream( aDestFile );
190 
191  if( !ostream.IsOk() ) // issue to create the file. Perhaps not writable dir
192  {
193  msg.Printf( _( "Unable to create archive file \"%s\"\n" ), aDestFile );
194  aReporter.Report( msg, RPT_SEVERITY_ERROR );
195  return false;
196  }
197 
198  wxZipOutputStream zipstream( ostream, -1, wxConvUTF8 );
199 
200  // Build list of filenames to put in zip archive
201  wxString currFilename;
202 
203  wxArrayString files;
204 
205  for( unsigned ii = 0; ii < arrayDim( extensionList ); ii++ )
206  wxDir::GetAllFiles( aSrcDir, &files, extensionList[ii] );
207 
208  files.Sort();
209 
210  unsigned long uncompressedBytes = 0;
211 
212  for( unsigned ii = 0; ii < files.GetCount(); ii++ )
213  {
214  wxFileSystem fsfile;
215 
216  wxFileName curr_fn( files[ii] );
217  curr_fn.MakeRelativeTo( aSrcDir );
218  currFilename = curr_fn.GetFullPath();
219 
220  // Read input file and add it to the zip file:
221  wxFSFile* infile = fsfile.OpenFile( wxFileSystem::FileNameToURL( curr_fn ) );
222 
223  if( infile )
224  {
225  zipstream.PutNextEntry( currFilename, infile->GetModificationTime() );
226  infile->GetStream()->Read( zipstream );
227  zipstream.CloseEntry();
228 
229  uncompressedBytes += infile->GetStream()->GetSize();
230 
231  if( aVerbose )
232  {
233  msg.Printf( _( "Archive file \"%s\"\n" ), currFilename );
234  aReporter.Report( msg, RPT_SEVERITY_INFO );
235  }
236 
237  delete infile;
238  }
239  else
240  {
241  if( aVerbose )
242  {
243  msg.Printf( _( "Archive file \"%s\": Failed!\n" ), currFilename );
244  aReporter.Report( msg, RPT_SEVERITY_ERROR );
245  }
246 
247  success = false;
248  }
249  }
250 
251  auto reportSize =
252  []( unsigned long aSize ) -> wxString
253  {
254  constexpr float KB = 1024.0;
255  constexpr float MB = KB * 1024.0;
256 
257  if( aSize >= MB )
258  return wxString::Format( wxT( "%0.2f MB" ), aSize / MB );
259  else if( aSize >= KB )
260  return wxString::Format( wxT( "%0.2f KB" ), aSize / KB );
261  else
262  return wxString::Format( wxT( "%lu bytes" ), aSize );
263  };
264 
265  size_t zipBytesCnt = ostream.GetSize();
266 
267  if( zipstream.Close() )
268  {
269  msg.Printf( _( "Zip archive \"%s\" created (%s uncompressed, %s compressed)\n" ), aDestFile,
270  reportSize( uncompressedBytes ), reportSize( zipBytesCnt ) );
271  aReporter.Report( msg, RPT_SEVERITY_INFO );
272  }
273  else
274  {
275  msg.Printf( wxT( "Unable to create archive \"%s\"\n" ), aDestFile );
276  aReporter.Report( msg, RPT_SEVERITY_ERROR );
277  success = false;
278  }
279 
280  wxSetWorkingDirectory( oldCwd );
281  return success;
282 }
static const std::vector< std::string > extensionList
bool Archive(const wxString &aSrcDir, const wxString &aDestFile, REPORTER &aReporter, bool aVerbose=true)
Creates an archive of the project.
REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:64
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
This file contains miscellaneous commonly used macros and functions.
bool Unarchive(const wxString &aSrcFile, const wxString &aDestDir, REPORTER &aReporter)
Extracts an archive of the current project over existing files Warning: this will overwrite files in ...
Definition of file extensions used in Kicad.
static bool CopyStreamData(wxInputStream &inputStream, wxOutputStream &outputStream, wxFileOffset size)
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Definition: macros.h:160
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
#define _(s)
Definition: 3d_actions.cpp:33