KiCad PCB EDA Suite
Loading...
Searching...
No Matches
gestfich.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) 2004 Jean-Pierre Charras, [email protected]
5 * Copyright (C) 2008 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
31#include <wx/mimetype.h>
32#include <wx/dir.h>
33
34#include <pgm_base.h>
35#include <confirm.h>
36#include <core/arraydim.h>
37#include <gestfich.h>
38#include <string_utils.h>
39#include <launch_ext.h>
40#include "wx/tokenzr.h"
41
42#include <wx/wfstream.h>
43#include <wx/fs_zip.h>
44#include <wx/zipstrm.h>
45
46#include <filesystem>
47
48void QuoteString( wxString& string )
49{
50 if( !string.StartsWith( wxT( "\"" ) ) )
51 {
52 string.Prepend ( wxT( "\"" ) );
53 string.Append ( wxT( "\"" ) );
54 }
55}
56
57
58wxString FindKicadFile( const wxString& shortname )
59{
60 // Test the presence of the file in the directory shortname of
61 // the KiCad binary path.
62#ifndef __WXMAC__
63 wxString fullFileName = Pgm().GetExecutablePath() + shortname;
64#else
65 wxString fullFileName = Pgm().GetExecutablePath() + wxT( "Contents/MacOS/" ) + shortname;
66#endif
67 if( wxFileExists( fullFileName ) )
68 return fullFileName;
69
70 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
71 {
72 wxFileName buildDir( Pgm().GetExecutablePath(), shortname );
73 buildDir.RemoveLastDir();
74#ifndef __WXMSW__
75 buildDir.AppendDir( shortname );
76#else
77 buildDir.AppendDir( shortname.BeforeLast( '.' ) );
78#endif
79
80 if( buildDir.GetDirs().Last() == "pl_editor" )
81 {
82 buildDir.RemoveLastDir();
83 buildDir.AppendDir( "pagelayout_editor" );
84 }
85
86 if( wxFileExists( buildDir.GetFullPath() ) )
87 return buildDir.GetFullPath();
88 }
89
90 // Test the presence of the file in the directory shortname
91 // defined by the environment variable KiCad.
92 if( Pgm().IsKicadEnvVariableDefined() )
93 {
94 fullFileName = Pgm().GetKicadEnvVariable() + shortname;
95
96 if( wxFileExists( fullFileName ) )
97 return fullFileName;
98 }
99
100#if defined( __WINDOWS__ )
101 // KiCad can be installed highly portably on Windows, anywhere and concurrently
102 // either the "kicad file" is immediately adjacent to the exe or it's not a valid install
103 return shortname;
104#else
105
106 // Path list for KiCad binary files
107 const static wxChar* possibilities[] = {
108#if defined( __WXMAC__ )
109 // all internal paths are relative to main bundle kicad.app
110 wxT( "Contents/Applications/pcbnew.app/Contents/MacOS/" ),
111 wxT( "Contents/Applications/eeschema.app/Contents/MacOS/" ),
112 wxT( "Contents/Applications/gerbview.app/Contents/MacOS/" ),
113 wxT( "Contents/Applications/bitmap2component.app/Contents/MacOS/" ),
114 wxT( "Contents/Applications/pcb_calculator.app/Contents/MacOS/" ),
115 wxT( "Contents/Applications/pl_editor.app/Contents/MacOS/" ),
116#else
117 wxT( "/usr/bin/" ),
118 wxT( "/usr/local/bin/" ),
119 wxT( "/usr/local/kicad/bin/" ),
120#endif
121 };
122
123 // find binary file from possibilities list:
124 for( unsigned i=0; i<arrayDim(possibilities); ++i )
125 {
126#ifndef __WXMAC__
127 fullFileName = possibilities[i] + shortname;
128#else
129 // make relative paths absolute
130 fullFileName = Pgm().GetExecutablePath() + possibilities[i] + shortname;
131#endif
132
133 if( wxFileExists( fullFileName ) )
134 return fullFileName;
135 }
136
137 return shortname;
138
139#endif
140}
141
142
143int ExecuteFile( const wxString& aEditorName, const wxString& aFileName, wxProcess* aCallback,
144 bool aFileForKicad )
145{
146 wxString fullEditorName;
147 std::vector<wxString> params;
148
149#ifdef __UNIX__
150 wxString param;
151 bool inSingleQuotes = false;
152 bool inDoubleQuotes = false;
153
154 auto pushParam =
155 [&]()
156 {
157 if( !param.IsEmpty() )
158 {
159 params.push_back( param );
160 param.clear();
161 }
162 };
163
164 for( wxUniChar ch : aEditorName )
165 {
166 if( inSingleQuotes )
167 {
168 if( ch == '\'' )
169 {
170 pushParam();
171 inSingleQuotes = false;
172 continue;
173 }
174 else
175 {
176 param += ch;
177 }
178 }
179 else if( inDoubleQuotes )
180 {
181 if( ch == '"' )
182 {
183 pushParam();
184 inDoubleQuotes = false;
185 }
186 else
187 {
188 param += ch;
189 }
190 }
191 else if( ch == '\'' )
192 {
193 pushParam();
194 inSingleQuotes = true;
195 }
196 else if( ch == '"' )
197 {
198 pushParam();
199 inDoubleQuotes = true;
200 }
201 else if( ch == ' ' )
202 {
203 pushParam();
204 }
205 else
206 {
207 param += ch;
208 }
209 }
210
211 pushParam();
212
213 if( aFileForKicad )
214 fullEditorName = FindKicadFile( params[0] );
215 else
216 fullEditorName = params[0];
217
218 params.erase( params.begin() );
219#else
220
221 if( aFileForKicad )
222 fullEditorName = FindKicadFile( aEditorName );
223 else
224 fullEditorName = aEditorName;
225#endif
226
227 if( wxFileExists( fullEditorName ) )
228 {
229 std::vector<const wchar_t*> args;
230
231 args.emplace_back( fullEditorName.wc_str() );
232
233 if( !params.empty() )
234 {
235 for( const wxString& p : params )
236 args.emplace_back( p.wc_str() );
237 }
238
239 if( !aFileName.IsEmpty() )
240 args.emplace_back( aFileName.wc_str() );
241
242 args.emplace_back( nullptr );
243
244 return wxExecute( const_cast<wchar_t**>( args.data() ), wxEXEC_ASYNC, aCallback );
245 }
246
247 wxString msg;
248 msg.Printf( _( "Command '%s' could not be found." ), fullEditorName );
249 DisplayErrorMessage( nullptr, msg );
250 return -1;
251}
252
253
254bool OpenPDF( const wxString& file )
255{
256 wxString msg;
257 wxString filename = file;
258
260
261 if( Pgm().UseSystemPdfBrowser() )
262 {
263 if( !LaunchExternal( filename ) )
264 {
265 msg.Printf( _( "Unable to find a PDF viewer for '%s'." ), filename );
266 DisplayErrorMessage( nullptr, msg );
267 return false;
268 }
269 }
270 else
271 {
272 const wchar_t* args[3];
273
274 args[0] = Pgm().GetPdfBrowserName().wc_str();
275 args[1] = filename.wc_str();
276 args[2] = nullptr;
277
278 if( wxExecute( const_cast<wchar_t**>( args ) ) == -1 )
279 {
280 msg.Printf( _( "Problem while running the PDF viewer '%s'." ), args[0] );
281 DisplayErrorMessage( nullptr, msg );
282 return false;
283 }
284 }
285
286 return true;
287}
288
289
290void KiCopyFile( const wxString& aSrcPath, const wxString& aDestPath, wxString& aErrors )
291{
292 if( !wxCopyFile( aSrcPath, aDestPath ) )
293 {
294 wxString msg;
295
296 if( !aErrors.IsEmpty() )
297 aErrors += "\n";
298
299 msg.Printf( _( "Cannot copy file '%s'." ), aDestPath );
300 aErrors += msg;
301 }
302}
303
304
305wxString QuoteFullPath( wxFileName& fn, wxPathFormat format )
306{
307 return wxT( "\"" ) + fn.GetFullPath( format ) + wxT( "\"" );
308}
309
310
311bool RmDirRecursive( const wxString& aFileName, wxString* aErrors )
312{
313 namespace fs = std::filesystem;
314
315 std::string rmDir = aFileName.ToStdString();
316
317 if( rmDir.length() < 3 )
318 {
319 if( aErrors )
320 *aErrors = _( "Invalid directory name, cannot remove root" );
321
322 return false;
323 }
324
325 if( !fs::exists( rmDir ) )
326 {
327 if( aErrors )
328 *aErrors = wxString::Format( _( "Directory '%s' does not exist" ), aFileName );
329
330 return false;
331 }
332
333 fs::path path( rmDir );
334
335 if( !fs::is_directory( path ) )
336 {
337 if( aErrors )
338 *aErrors = wxString::Format( _( "'%s' is not a directory" ), aFileName );
339
340 return false;
341 }
342
343 try
344 {
345 fs::remove_all( path );
346 }
347 catch( const fs::filesystem_error& e )
348 {
349 if( aErrors )
350 *aErrors = wxString::Format( _( "Error removing directory '%s': %s" ),
351 aFileName, e.what() );
352
353 return false;
354 }
355
356 return true;
357}
358
359
360bool CopyDirectory( const wxString& aSourceDir, const wxString& aDestDir, wxString& aErrors )
361{
362 wxDir dir( aSourceDir );
363
364 if( !dir.IsOpened() )
365 {
366 aErrors += wxString::Format( _( "Could not open source directory: %s" ), aSourceDir );
367 aErrors += wxT( "\n" );
368 return false;
369 }
370
371 if( !wxFileName::Mkdir( aDestDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
372 {
373 aErrors += wxString::Format( _( "Could not create destination directory: %s" ), aDestDir );
374 aErrors += wxT( "\n" );
375 return false;
376 }
377
378 wxString filename;
379 bool cont = dir.GetFirst( &filename );
380
381 while( cont )
382 {
383 wxString sourcePath = aSourceDir + wxFileName::GetPathSeparator() + filename;
384 wxString destPath = aDestDir + wxFileName::GetPathSeparator() + filename;
385
386 if( wxFileName::DirExists( sourcePath ) )
387 {
388 // Recursively copy subdirectories
389 if( !CopyDirectory( sourcePath, destPath, aErrors ) )
390 return false;
391 }
392 else
393 {
394 // Copy files
395 if( !wxCopyFile( sourcePath, destPath ) )
396 {
397 aErrors += wxString::Format( _( "Could not copy file: %s to %s" ),
398 sourcePath,
399 destPath );
400 return false;
401 }
402 }
403
404 cont = dir.GetNext( &filename );
405 }
406
407 return true;
408}
409
410
411bool CopyFilesOrDirectory( const wxString& aSourcePath, const wxString& aDestDir, wxString& aErrors,
412 int& aFileCopiedCount, const std::vector<wxString>& aExclusions )
413{
414 // Parse source path and determine if it's a directory
415 wxFileName sourceFn( aSourcePath );
416 wxString sourcePath = sourceFn.GetFullPath();
417 bool isSourceDirectory = wxFileName::DirExists( sourcePath );
418 wxString baseDestDir = aDestDir;
419
420 auto performCopy = [&]( const wxString& src, const wxString& dest ) -> bool
421 {
422 if( wxCopyFile( src, dest ) )
423 {
424 aFileCopiedCount++;
425 return true;
426 }
427
428 aErrors += wxString::Format( _( "Could not copy file: %s to %s" ), src, dest );
429 aErrors += wxT( "\n" );
430 return false;
431 };
432
433 auto processEntries = [&]( const wxString& srcDir, const wxString& pattern,
434 const wxString& destDir ) -> bool
435 {
436 wxDir dir( srcDir );
437
438 if( !dir.IsOpened() )
439 {
440 aErrors += wxString::Format( _( "Could not open source directory: %s" ), srcDir );
441 aErrors += wxT( "\n" );
442 return false;
443 }
444
445 wxString filename;
446 bool success = true;
447
448 // Find all entries matching pattern (files + directories + hidden items)
449 bool cont = dir.GetFirst( &filename, pattern, wxDIR_FILES | wxDIR_DIRS | wxDIR_HIDDEN );
450
451 while( cont )
452 {
453 const wxString entrySrc = srcDir + wxFileName::GetPathSeparator() + filename;
454 const wxString entryDest = destDir + wxFileName::GetPathSeparator() + filename;
455
456 // Apply exclusion filters
457 bool exclude =
458 filename.Matches( wxT( "~*.lck" ) ) || filename.Matches( wxT( "*.lck" ) );
459
460 for( const auto& exclusion : aExclusions )
461 {
462 if( entrySrc.Matches( exclusion ) )
463 {
464 exclude = true;
465 break;
466 }
467 }
468
469 if( !exclude )
470 {
471 if( wxFileName::DirExists( entrySrc ) )
472 {
473 // Recursively process subdirectories
474 if( !CopyFilesOrDirectory( entrySrc, destDir, aErrors, aFileCopiedCount,
475 aExclusions ) )
476 {
477 aErrors += wxString::Format( _( "Could not copy directory: %s to %s" ),
478 entrySrc, entryDest );
479 aErrors += wxT( "\n" );
480
481 success = false;
482 }
483 }
484 else
485 {
486 // Copy individual files
487 if( !performCopy( entrySrc, entryDest ) )
488 {
489 success = false;
490 }
491 }
492 }
493
494 cont = dir.GetNext( &filename );
495 }
496
497 return success;
498 };
499
500 // If copying a directory, append its name to destination path
501 if( isSourceDirectory )
502 {
503 wxString sourceDirName = sourceFn.GetFullName();
504 baseDestDir = wxFileName( aDestDir, sourceDirName ).GetFullPath();
505 }
506
507 // Create destination directory hierarchy
508 if( !wxFileName::Mkdir( baseDestDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
509 {
510 aErrors +=
511 wxString::Format( _( "Could not create destination directory: %s" ), baseDestDir );
512 aErrors += wxT( "\n" );
513
514 return false;
515 }
516
517 // Execute appropriate copy operation based on source type
518 if( !isSourceDirectory )
519 {
520 const wxString fileName = sourceFn.GetFullName();
521
522 // Handle wildcard patterns in filenames
523 if( fileName.Contains( '*' ) || fileName.Contains( '?' ) )
524 {
525 const wxString dirPath = sourceFn.GetPath();
526
527 if( !wxFileName::DirExists( dirPath ) )
528 {
529 aErrors += wxString::Format( _( "Source directory does not exist: %s" ), dirPath );
530 aErrors += wxT( "\n" );
531
532 return false;
533 }
534 // Process all matching files in source directory
535 return processEntries( dirPath, fileName, baseDestDir );
536 }
537
538 // Single file copy operation
539 return performCopy( sourcePath, wxFileName( baseDestDir, fileName ).GetFullPath() );
540 }
541
542 // Full directory copy operation
543 return processEntries( sourcePath, wxEmptyString, baseDestDir );
544}
545
546
547bool AddDirectoryToZip( wxZipOutputStream& aZip, const wxString& aSourceDir, wxString& aErrors,
548 const wxString& aParentDir )
549{
550 wxDir dir( aSourceDir );
551
552 if( !dir.IsOpened() )
553 {
554 aErrors += wxString::Format( _( "Could not open source directory: %s" ), aSourceDir );
555 aErrors += "\n";
556 return false;
557 }
558
559 wxString filename;
560 bool cont = dir.GetFirst( &filename );
561
562 while( cont )
563 {
564 wxString sourcePath = aSourceDir + wxFileName::GetPathSeparator() + filename;
565 wxString zipPath = aParentDir + filename;
566
567 if( wxFileName::DirExists( sourcePath ) )
568 {
569 // Add directory entry to the ZIP file
570 aZip.PutNextDirEntry( zipPath + "/" );
571
572 // Recursively add subdirectories
573 if( !AddDirectoryToZip( aZip, sourcePath, aErrors, zipPath + "/" ) )
574 return false;
575 }
576 else
577 {
578 // Add file entry to the ZIP file
579 aZip.PutNextEntry( zipPath );
580 wxFFileInputStream fileStream( sourcePath );
581
582 if( !fileStream.IsOk() )
583 {
584 aErrors += wxString::Format( _( "Could not read file: %s" ), sourcePath );
585 return false;
586 }
587
588 aZip.Write( fileStream );
589 }
590
591 cont = dir.GetNext( &filename );
592 }
593
594 return true;
595}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
virtual const wxString & GetKicadEnvVariable() const
Definition: pgm_base.h:170
virtual void ReadPdfBrowserInfos()
Read the PDF browser choice from the common configuration.
Definition: pgm_base.cpp:1056
virtual const wxString & GetPdfBrowserName() const
Definition: pgm_base.h:176
virtual const wxString & GetExecutablePath() const
Definition: pgm_base.cpp:1050
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
This file is part of the common library.
#define _(s)
wxString FindKicadFile(const wxString &shortname)
Search the executable file shortname in KiCad binary path and return full file name if found or short...
Definition: gestfich.cpp:58
wxString QuoteFullPath(wxFileName &fn, wxPathFormat format)
Quote return value of wxFileName::GetFullPath().
Definition: gestfich.cpp:305
bool OpenPDF(const wxString &file)
Run the PDF viewer and display a PDF file.
Definition: gestfich.cpp:254
void KiCopyFile(const wxString &aSrcPath, const wxString &aDestPath, wxString &aErrors)
Definition: gestfich.cpp:290
int ExecuteFile(const wxString &aEditorName, const wxString &aFileName, wxProcess *aCallback, bool aFileForKicad)
Call the executable file aEditorName with the parameter aFileName.
Definition: gestfich.cpp:143
bool CopyFilesOrDirectory(const wxString &aSourcePath, const wxString &aDestDir, wxString &aErrors, int &aFileCopiedCount, const std::vector< wxString > &aExclusions)
Definition: gestfich.cpp:411
bool RmDirRecursive(const wxString &aFileName, wxString *aErrors)
Remove the directory aDirName and all its contents including subdirectories and their files.
Definition: gestfich.cpp:311
void QuoteString(wxString &string)
Add un " to the start and the end of string (if not already done).
Definition: gestfich.cpp:48
bool CopyDirectory(const wxString &aSourceDir, const wxString &aDestDir, wxString &aErrors)
Copy a directory and its contents to another directory.
Definition: gestfich.cpp:360
bool AddDirectoryToZip(wxZipOutputStream &aZip, const wxString &aSourceDir, wxString &aErrors, const wxString &aParentDir)
Add a directory and its contents to a zip file.
Definition: gestfich.cpp:547
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
Definition: launch_ext.cpp:25
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE