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, see <https://www.gnu.org/licenses/>.
20 */
21
26
27#include <wx/mimetype.h>
28#include <wx/dir.h>
29
30#include <pgm_base.h>
31#include <confirm.h>
32#include <core/arraydim.h>
33#include <gestfich.h>
34#include <string_utils.h>
35#include <launch_ext.h>
36#include "wx/tokenzr.h"
37#include <richio.h>
38#include <sexpr/sexpr.h>
39#include <sexpr/sexpr_parser.h>
40
41#include <wx/wfstream.h>
42#include <wx/fs_zip.h>
43#include <wx/zipstrm.h>
44
45#include <filesystem>
46#include <string>
47#include <system_error>
48#include <unordered_set>
49#include <core/kicad_algo.h>
50
51void QuoteString( wxString& string )
52{
53 if( !string.StartsWith( wxT( "\"" ) ) )
54 {
55 string.Prepend ( wxT( "\"" ) );
56 string.Append ( wxT( "\"" ) );
57 }
58}
59
60
61wxString FindKicadFile( const wxString& shortname )
62{
63 // Test the presence of the file in the directory shortname of
64 // the KiCad binary path.
65#ifndef __WXMAC__
66 wxString fullFileName = Pgm().GetExecutablePath() + shortname;
67#else
68 wxString fullFileName = Pgm().GetExecutablePath() + wxT( "Contents/MacOS/" ) + shortname;
69#endif
70 if( wxFileExists( fullFileName ) )
71 return fullFileName;
72
73 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
74 {
75 wxFileName buildDir( Pgm().GetExecutablePath(), shortname );
76
77#ifdef __WXMAC__
78 if( !buildDir.GetDirs().IsEmpty()
79 && buildDir.GetDirs().Last().Lower().EndsWith( wxT( ".app" ) ) )
80 {
81 buildDir.RemoveLastDir();
82 }
83#endif
84 buildDir.RemoveLastDir();
85#ifndef __WXMSW__
86 buildDir.AppendDir( shortname );
87#else
88 buildDir.AppendDir( shortname.BeforeLast( '.' ) );
89#endif
90
91 if( buildDir.GetDirs().Last() == "pl_editor" )
92 {
93 buildDir.RemoveLastDir();
94 buildDir.AppendDir( "pagelayout_editor" );
95 }
96
97#ifdef __WXMAC__
98 buildDir.AppendDir( shortname + wxT( ".app" ) );
99 buildDir.AppendDir( wxT( "Contents" ) );
100 buildDir.AppendDir( wxT( "MacOS" ) );
101#endif
102
103 if( wxFileExists( buildDir.GetFullPath() ) )
104 return buildDir.GetFullPath();
105 }
106
107 // Test the presence of the file in the directory shortname
108 // defined by the environment variable KiCad.
109 if( Pgm().IsKicadEnvVariableDefined() )
110 {
111 fullFileName = Pgm().GetKicadEnvVariable() + shortname;
112
113 if( wxFileExists( fullFileName ) )
114 return fullFileName;
115 }
116
117#if defined( __WINDOWS__ )
118 // KiCad can be installed highly portably on Windows, anywhere and concurrently
119 // either the "kicad file" is immediately adjacent to the exe or it's not a valid install
120 return shortname;
121#else
122
123 // Path list for KiCad binary files
124 const static wxChar* possibilities[] = {
125#if defined( __WXMAC__ )
126 // all internal paths are relative to main bundle kicad.app
127 wxT( "Contents/Applications/pcbnew.app/Contents/MacOS/" ),
128 wxT( "Contents/Applications/eeschema.app/Contents/MacOS/" ),
129 wxT( "Contents/Applications/gerbview.app/Contents/MacOS/" ),
130 wxT( "Contents/Applications/bitmap2component.app/Contents/MacOS/" ),
131 wxT( "Contents/Applications/pcb_calculator.app/Contents/MacOS/" ),
132 wxT( "Contents/Applications/pl_editor.app/Contents/MacOS/" ),
133#else
134 wxT( "/usr/bin/" ),
135 wxT( "/usr/local/bin/" ),
136 wxT( "/usr/local/kicad/bin/" ),
137#endif
138 };
139
140 // find binary file from possibilities list:
141 for( unsigned i=0; i<arrayDim(possibilities); ++i )
142 {
143#ifndef __WXMAC__
144 fullFileName = possibilities[i] + shortname;
145#else
146 // make relative paths absolute
147 fullFileName = Pgm().GetExecutablePath() + possibilities[i] + shortname;
148#endif
149
150 if( wxFileExists( fullFileName ) )
151 return fullFileName;
152 }
153
154 return shortname;
155
156#endif
157}
158
159
160int ExecuteFile( const wxString& aEditorName, const wxString& aFileName, wxProcess* aCallback,
161 bool aFileForKicad )
162{
163 wxString fullEditorName;
164 std::vector<wxString> params;
165
166#ifdef __UNIX__
167 wxString param;
168 bool inSingleQuotes = false;
169 bool inDoubleQuotes = false;
170
171 auto pushParam =
172 [&]()
173 {
174 if( !param.IsEmpty() )
175 {
176 params.push_back( param );
177 param.clear();
178 }
179 };
180
181 for( wxUniChar ch : aEditorName )
182 {
183 if( inSingleQuotes )
184 {
185 if( ch == '\'' )
186 {
187 pushParam();
188 inSingleQuotes = false;
189 continue;
190 }
191 else
192 {
193 param += ch;
194 }
195 }
196 else if( inDoubleQuotes )
197 {
198 if( ch == '"' )
199 {
200 pushParam();
201 inDoubleQuotes = false;
202 }
203 else
204 {
205 param += ch;
206 }
207 }
208 else if( ch == '\'' )
209 {
210 pushParam();
211 inSingleQuotes = true;
212 }
213 else if( ch == '"' )
214 {
215 pushParam();
216 inDoubleQuotes = true;
217 }
218 else if( ch == ' ' )
219 {
220 pushParam();
221 }
222 else
223 {
224 param += ch;
225 }
226 }
227
228 pushParam();
229
230 if( aFileForKicad )
231 fullEditorName = FindKicadFile( params[0] );
232 else
233 fullEditorName = params[0];
234
235 params.erase( params.begin() );
236#else
237
238 if( aFileForKicad )
239 fullEditorName = FindKicadFile( aEditorName );
240 else
241 fullEditorName = aEditorName;
242#endif
243
244 if( wxFileExists( fullEditorName ) )
245 {
246 std::vector<const wchar_t*> args;
247
248 args.emplace_back( fullEditorName.wc_str() );
249
250 if( !params.empty() )
251 {
252 for( const wxString& p : params )
253 args.emplace_back( p.wc_str() );
254 }
255
256 if( !aFileName.IsEmpty() )
257 args.emplace_back( aFileName.wc_str() );
258
259 args.emplace_back( nullptr );
260
261 return wxExecute( const_cast<wchar_t**>( args.data() ), wxEXEC_ASYNC, aCallback );
262 }
263
264 wxString msg;
265 msg.Printf( _( "Command '%s' could not be found." ), fullEditorName );
266 DisplayErrorMessage( nullptr, msg );
267 return -1;
268}
269
270
271bool OpenPDF( const wxString& file )
272{
273 wxString msg;
274 wxString filename = file;
275
277
278 if( Pgm().UseSystemPdfBrowser() )
279 {
280 if( !LaunchExternal( filename ) )
281 {
282 msg.Printf( _( "Unable to find a PDF viewer for '%s'." ), filename );
283 DisplayErrorMessage( nullptr, msg );
284 return false;
285 }
286 }
287 else
288 {
289 const wchar_t* args[3];
290
291 args[0] = Pgm().GetPdfBrowserName().wc_str();
292 args[1] = filename.wc_str();
293 args[2] = nullptr;
294
295 if( wxExecute( const_cast<wchar_t**>( args ) ) == -1 )
296 {
297 msg.Printf( _( "Problem while running the PDF viewer '%s'." ), args[0] );
298 DisplayErrorMessage( nullptr, msg );
299 return false;
300 }
301 }
302
303 return true;
304}
305
306
307void KiCopyFile( const wxString& aSrcPath, const wxString& aDestPath, wxString& aErrors )
308{
309 if( !wxCopyFile( aSrcPath, aDestPath ) )
310 {
311 wxString msg;
312
313 if( !aErrors.IsEmpty() )
314 aErrors += "\n";
315
316 msg.Printf( _( "Cannot copy file '%s'." ), aDestPath );
317 aErrors += msg;
318 }
319}
320
321
322static void traverseSEXPR( SEXPR::SEXPR* aNode, const std::function<void( SEXPR::SEXPR* )>& aVisitor )
323{
324 aVisitor( aNode );
325
326 if( aNode->IsList() )
327 {
328 for( unsigned i = 0; i < aNode->GetNumberOfChildren(); i++ )
329 traverseSEXPR( aNode->GetChild( i ), aVisitor );
330 }
331}
332
333
334void CopySexprFile( const wxString& aSrcPath, const wxString& aDestPath,
335 std::function<bool( const std::string& token, wxString& value )> aCallback,
336 wxString& aErrors )
337{
338 bool success = false;
339
340 try
341 {
342 SEXPR::PARSER parser;
343 std::unique_ptr<SEXPR::SEXPR> sexpr( parser.ParseFromFile( TO_UTF8( aSrcPath ) ) );
344
345 traverseSEXPR( sexpr.get(),
346 [&]( SEXPR::SEXPR* node )
347 {
348 if( node->IsList() && node->GetNumberOfChildren() > 1 && node->GetChild( 0 )->IsSymbol() )
349 {
350 std::string token = node->GetChild( 0 )->GetSymbol();
351 SEXPR::SEXPR_STRING* pathNode = dynamic_cast<SEXPR::SEXPR_STRING*>( node->GetChild( 1 ) );
352 SEXPR::SEXPR_SYMBOL* symNode = dynamic_cast<SEXPR::SEXPR_SYMBOL*>( node->GetChild( 1 ) );
353 wxString path;
354
355 if( pathNode )
356 path = pathNode->m_value;
357 else if( symNode )
358 path = symNode->m_value;
359
360 if( aCallback( token, path ) )
361 {
362 if( pathNode )
363 pathNode->m_value = path;
364 else if( symNode )
365 symNode->m_value = path;
366 }
367 }
368 } );
369
370
371 // Pass through the pretifier to ensure format is the same as when a file is saved by a frame
372 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( aDestPath );
373
374 // Format as a string to prevent format string attacks
375 formatter.Print( "%s", sexpr->AsString( 0 ).c_str() );
376
377 success = formatter.Finish();
378 }
379 catch( ... )
380 {
381 success = false;
382 }
383
384 if( !success )
385 {
386 wxString msg;
387
388 if( !aErrors.empty() )
389 aErrors += wxS( "\n" );
390
391 msg.Printf( _( "Cannot copy file '%s'." ), aDestPath );
392 aErrors += msg;
393 }
394}
395
396
397wxString QuoteFullPath( wxFileName& fn, wxPathFormat format )
398{
399 return wxT( "\"" ) + fn.GetFullPath( format ) + wxT( "\"" );
400}
401
402
403bool RmDirRecursive( const wxString& aFileName, wxString* aErrors )
404{
405 namespace fs = std::filesystem;
406
407 std::string rmDir = aFileName.ToStdString();
408
409 if( rmDir.length() < 3 )
410 {
411 if( aErrors )
412 *aErrors = _( "Invalid directory name, cannot remove root" );
413
414 return false;
415 }
416
417 if( !fs::exists( rmDir ) )
418 {
419 if( aErrors )
420 *aErrors = wxString::Format( _( "Directory '%s' does not exist" ), aFileName );
421
422 return false;
423 }
424
425 fs::path path( rmDir );
426
427 if( !fs::is_directory( path ) )
428 {
429 if( aErrors )
430 *aErrors = wxString::Format( _( "'%s' is not a directory" ), aFileName );
431
432 return false;
433 }
434
435 try
436 {
437 fs::remove_all( path );
438 }
439 catch( const fs::filesystem_error& e )
440 {
441 if( aErrors )
442 *aErrors = wxString::Format( _( "Error removing directory '%s': %s" ), aFileName, e.what() );
443
444 return false;
445 }
446
447 return true;
448}
449
450
451bool CopyDirectory( const wxString& aSourceDir, const wxString& aDestDir,
452 const std::vector<wxString>& aPathsWithOverwriteDisallowed, wxString& aErrors )
453{
454 wxDir dir( aSourceDir );
455
456 if( !dir.IsOpened() )
457 {
458 aErrors += wxString::Format( _( "Could not open source directory: %s" ), aSourceDir );
459 aErrors += wxT( "\n" );
460 return false;
461 }
462
463 if( !wxFileName::Mkdir( aDestDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
464 {
465 aErrors += wxString::Format( _( "Could not create destination directory: %s" ), aDestDir );
466 aErrors += wxT( "\n" );
467 return false;
468 }
469
470 wxString filename;
471 bool cont = dir.GetFirst( &filename );
472
473 while( cont )
474 {
475 wxString sourcePath = dir.GetNameWithSep() + filename;
476 wxString destPath = aDestDir + wxFileName::GetPathSeparator() + filename;
477
478 if( wxFileName::DirExists( sourcePath ) )
479 {
480 // Recursively copy subdirectories
481 if( !CopyDirectory( sourcePath, destPath, aPathsWithOverwriteDisallowed, aErrors ) )
482 return false;
483 }
484 else
485 {
486 // Copy files
487 if( alg::contains( aPathsWithOverwriteDisallowed, sourcePath ) && wxFileExists( destPath ) )
488 {
489 // Presumably user does not want an error on a no-overwrite condition....
490 }
491 else if( !wxCopyFile( sourcePath, destPath ) )
492 {
493 aErrors += wxString::Format( _( "Could not copy file: %s to %s" ), sourcePath, destPath );
494 return false;
495 }
496 }
497
498 cont = dir.GetNext( &filename );
499 }
500
501 return true;
502}
503
504
505bool CopyFilesOrDirectory( const wxString& aSourcePath, const wxString& aDestDir, bool aAllowOverwrites,
506 wxString& aErrors, std::vector<wxString>& aPathsWritten )
507{
508 // Parse source path and determine if it's a directory
509 wxFileName sourceFn( aSourcePath );
510 wxString sourcePath = sourceFn.GetFullPath();
511 bool isSourceDirectory = wxFileName::DirExists( sourcePath );
512 wxString baseDestDir = aDestDir;
513
514 auto performCopy =
515 [&]( const wxString& src, const wxString& dest ) -> bool
516 {
517 if( wxCopyFile( src, dest, aAllowOverwrites ) )
518 {
519 aPathsWritten.push_back( dest );
520 return true;
521 }
522
523 aErrors += wxString::Format( _( "Could not copy file: %s to %s" ), src, dest );
524 aErrors += wxT( "\n" );
525 return false;
526 };
527
528 auto processEntries =
529 [&]( const wxString& srcDir, const wxString& pattern, const wxString& destDir ) -> bool
530 {
531 wxDir dir( srcDir );
532
533 if( !dir.IsOpened() )
534 {
535 aErrors += wxString::Format( _( "Could not open source directory: %s" ), srcDir );
536 aErrors += wxT( "\n" );
537 return false;
538 }
539
540 wxString filename;
541 bool success = true;
542
543 // Find all entries matching pattern (files + directories + hidden items)
544 bool cont = dir.GetFirst( &filename, pattern, wxDIR_FILES | wxDIR_DIRS | wxDIR_HIDDEN );
545
546 while( cont )
547 {
548 const wxString entrySrc = srcDir + wxFileName::GetPathSeparator() + filename;
549 const wxString entryDest = destDir + wxFileName::GetPathSeparator() + filename;
550
551 if( !filename.Matches( wxT( "~*.lck" ) ) && !filename.Matches( wxT( "*.lck" ) ) )
552 {
553 if( wxFileName::DirExists( entrySrc ) )
554 {
555 // Recursively process subdirectories
556 if( !CopyFilesOrDirectory( entrySrc, destDir, aAllowOverwrites, aErrors, aPathsWritten ) )
557 {
558 aErrors += wxString::Format( _( "Could not copy directory: %s to %s" ),
559 entrySrc, entryDest );
560 aErrors += wxT( "\n" );
561
562 success = false;
563 }
564 }
565 else
566 {
567 // Copy individual files
568 if( !performCopy( entrySrc, entryDest ) )
569 {
570 success = false;
571 }
572 }
573 }
574
575 cont = dir.GetNext( &filename );
576 }
577
578 return success;
579 };
580
581 // If copying a directory, append its name to destination path
582 if( isSourceDirectory )
583 {
584 wxString sourceDirName = sourceFn.GetFullName();
585 baseDestDir = wxFileName( aDestDir, sourceDirName ).GetFullPath();
586 }
587
588 // Create destination directory hierarchy
589 if( !wxFileName::Mkdir( baseDestDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
590 {
591 aErrors += wxString::Format( _( "Could not create destination directory: %s" ), baseDestDir );
592 aErrors += wxT( "\n" );
593
594 return false;
595 }
596
597 // Execute appropriate copy operation based on source type
598 if( !isSourceDirectory )
599 {
600 const wxString fileName = sourceFn.GetFullName();
601
602 // Handle wildcard patterns in filenames
603 if( fileName.Contains( '*' ) || fileName.Contains( '?' ) )
604 {
605 const wxString dirPath = sourceFn.GetPath();
606
607 if( !wxFileName::DirExists( dirPath ) )
608 {
609 aErrors += wxString::Format( _( "Source directory does not exist: %s" ), dirPath );
610 aErrors += wxT( "\n" );
611
612 return false;
613 }
614
615 // Process all matching files in source directory
616 return processEntries( dirPath, fileName, baseDestDir );
617 }
618
619 // Single file copy operation
620 return performCopy( sourcePath, wxFileName( baseDestDir, fileName ).GetFullPath() );
621 }
622
623 // Full directory copy operation
624 return processEntries( sourcePath, wxEmptyString, baseDestDir );
625}
626
627
628bool AddDirectoryToZip( wxZipOutputStream& aZip, const wxString& aSourceDir, wxString& aErrors,
629 const wxString& aParentDir )
630{
631 wxDir dir( aSourceDir );
632
633 if( !dir.IsOpened() )
634 {
635 aErrors += wxString::Format( _( "Could not open source directory: %s" ), aSourceDir );
636 aErrors += "\n";
637 return false;
638 }
639
640 wxString filename;
641 bool cont = dir.GetFirst( &filename );
642
643 while( cont )
644 {
645 wxString sourcePath = aSourceDir + wxFileName::GetPathSeparator() + filename;
646 wxString zipPath = aParentDir + filename;
647
648 if( wxFileName::DirExists( sourcePath ) )
649 {
650 // Add directory entry to the ZIP file
651 aZip.PutNextDirEntry( zipPath + "/" );
652
653 // Recursively add subdirectories
654 if( !AddDirectoryToZip( aZip, sourcePath, aErrors, zipPath + "/" ) )
655 return false;
656 }
657 else
658 {
659 // Add file entry to the ZIP file
660 aZip.PutNextEntry( zipPath );
661 wxFFileInputStream fileStream( sourcePath );
662
663 if( !fileStream.IsOk() )
664 {
665 aErrors += wxString::Format( _( "Could not read file: %s" ), sourcePath );
666 return false;
667 }
668
669 aZip.Write( fileStream );
670 }
671
672 cont = dir.GetNext( &filename );
673 }
674
675 return true;
676}
677
678
679namespace
680{
681
682std::filesystem::path toFsPath( const wxString& aPath )
683{
684#ifdef __WXMSW__
685 return std::filesystem::path( std::wstring( aPath.wc_str() ) );
686#else
687 return std::filesystem::path( aPath.utf8_string() );
688#endif
689}
690
691
692// Best-effort canonicalisation. Empty path means "couldn't resolve"
693// (broken symlink, ELOOP, etc.); callers treat that as "skip rather than
694// risk recursing".
695std::filesystem::path canonicalPath( const std::filesystem::path& aPath )
696{
697 std::error_code ec;
698 std::filesystem::path canon = std::filesystem::weakly_canonical( aPath, ec );
699
700 return ec ? std::filesystem::path() : canon;
701}
702
703
704// True if @p aAncestor is equal to or an ancestor of @p aDescendant. Both
705// must already be canonical so that "/a/b" and "/a/b/c" share a prefix
706// component-wise.
707bool isAncestorOrSame( const std::filesystem::path& aAncestor,
708 const std::filesystem::path& aDescendant )
709{
710 auto a = aAncestor.begin();
711 auto d = aDescendant.begin();
712
713 for( ; a != aAncestor.end() && d != aDescendant.end(); ++a, ++d )
714 {
715 if( *a != *d )
716 return false;
717 }
718
719 return a == aAncestor.end();
720}
721
722
723// Shared traverser for CollectFilesLoopSafe / CollectSubdirsLoopSafe. Records
724// files, directories, or both into @p aOutput while deduplicating visited
725// directories by canonical path so recursive symlinks terminate.
726class LOOP_SAFE_COLLECTOR : public wxDirTraverser
727{
728public:
729 LOOP_SAFE_COLLECTOR( wxArrayString& aOutput, const wxString& aRoot, bool aCollectFiles,
730 bool aCollectDirs ) :
731 m_output( aOutput ),
732 m_root( canonicalPath( toFsPath( aRoot ) ) ),
733 m_collectFiles( aCollectFiles ),
734 m_collectDirs( aCollectDirs )
735 {
736 m_visited.reserve( 256 );
737
738 if( !m_root.empty() )
739 m_visited.insert( m_root.generic_string() );
740 }
741
742 wxDirTraverseResult OnFile( const wxString& aFilename ) override
743 {
744 if( m_collectFiles )
745 m_output.Add( aFilename );
746
747 return wxDIR_CONTINUE;
748 }
749
750 wxDirTraverseResult OnDir( const wxString& aDirname ) override
751 {
752 const std::filesystem::path raw = toFsPath( aDirname );
753
754 // Fast path: a real (non-symlink) subdir can't introduce a cycle by
755 // itself, so skip the per-component weakly_canonical and use the
756 // string-only lexically_normal as the dedup key. Cold-cache walks
757 // of large model libraries pay one lstat per dir instead of one per
758 // path component.
759 std::error_code ec;
760 const bool isLink = std::filesystem::is_symlink( raw, ec );
761
762 std::filesystem::path key;
763
764 if( isLink && !ec )
765 {
766 const std::filesystem::path canon = canonicalPath( raw );
767
768 if( canon.empty() )
769 return wxDIR_IGNORE;
770
771 // Refuse to escape the scan tree. Stops Wine 'dosdevices/z: -> /'
772 // and similar root-escape symlinks from walking the whole disk
773 // before the visited-set catches the eventual re-entry.
774 if( !m_root.empty() && isAncestorOrSame( canon, m_root ) )
775 return wxDIR_IGNORE;
776
777 key = canon;
778 }
779 else
780 {
781 key = raw.lexically_normal();
782 }
783
784 if( !m_visited.insert( key.generic_string() ).second )
785 return wxDIR_IGNORE;
786
787 if( m_collectDirs )
788 m_output.Add( aDirname );
789
790 return wxDIR_CONTINUE;
791 }
792
793private:
794 wxArrayString& m_output;
795 std::filesystem::path m_root;
796 bool m_collectFiles;
797 bool m_collectDirs;
798 std::unordered_set<std::string> m_visited;
799};
800
801
802void traverseLoopSafe( const wxString& aRoot, wxArrayString& aOutput, bool aCollectFiles,
803 bool aCollectDirs, const wxString& aFileSpec, int aFlags )
804{
805 wxDir dir( aRoot );
806
807 if( !dir.IsOpened() )
808 return;
809
810 LOOP_SAFE_COLLECTOR collector( aOutput, aRoot, aCollectFiles, aCollectDirs );
811 dir.Traverse( collector, aFileSpec, aFlags );
812}
813
814} // namespace
815
816
817void CollectFilesLoopSafe( const wxString& aRoot, wxArrayString& aFiles, const wxString& aFileSpec,
818 int aFlags )
819{
820 // Force wxDIR_FILES so files are reported and wxDIR_DIRS so Traverse descends
821 // into subdirectories; the collector keeps directories out of the file list
822 // and breaks loops. aFlags carries the caller's wxDIR_HIDDEN choice.
823 traverseLoopSafe( aRoot, aFiles, true, false, aFileSpec, aFlags | wxDIR_FILES | wxDIR_DIRS );
824}
825
826
827void CollectSubdirsLoopSafe( const wxString& aRoot, wxArrayString& aDirs, int aFlags )
828{
829 traverseLoopSafe( aRoot, aDirs, false, true, wxEmptyString, aFlags | wxDIR_DIRS );
830}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition arraydim.h:27
int PRINTF_FUNC_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition richio.cpp:422
virtual const wxString & GetKicadEnvVariable() const
Definition pgm_base.h:171
virtual void ReadPdfBrowserInfos()
Read the PDF browser choice from the common configuration.
Definition pgm_base.cpp:860
virtual const wxString & GetPdfBrowserName() const
Definition pgm_base.h:177
virtual const wxString & GetExecutablePath() const
Definition pgm_base.cpp:854
bool Finish() override
Runs prettification over the buffered bytes, writes them to the sibling temp file,...
Definition richio.cpp:696
std::unique_ptr< SEXPR > ParseFromFile(const std::string &aFilename)
size_t GetNumberOfChildren() const
Definition sexpr.cpp:72
bool IsList() const
Definition sexpr.h:49
SEXPR * GetChild(size_t aIndex) const
Definition sexpr.cpp:50
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
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:61
wxString QuoteFullPath(wxFileName &fn, wxPathFormat format)
Quote return value of wxFileName::GetFullPath().
Definition gestfich.cpp:397
void CopySexprFile(const wxString &aSrcPath, const wxString &aDestPath, std::function< bool(const std::string &token, wxString &value)> aCallback, wxString &aErrors)
Definition gestfich.cpp:334
bool OpenPDF(const wxString &file)
Run the PDF viewer and display a PDF file.
Definition gestfich.cpp:271
void KiCopyFile(const wxString &aSrcPath, const wxString &aDestPath, wxString &aErrors)
Definition gestfich.cpp:307
int ExecuteFile(const wxString &aEditorName, const wxString &aFileName, wxProcess *aCallback, bool aFileForKicad)
Call the executable file aEditorName with the parameter aFileName.
Definition gestfich.cpp:160
bool RmDirRecursive(const wxString &aFileName, wxString *aErrors)
Remove the directory aDirName and all its contents including subdirectories and their files.
Definition gestfich.cpp:403
void QuoteString(wxString &string)
Add un " to the start and the end of string (if not already done).
Definition gestfich.cpp:51
bool CopyFilesOrDirectory(const wxString &aSourcePath, const wxString &aDestDir, bool aAllowOverwrites, wxString &aErrors, std::vector< wxString > &aPathsWritten)
Definition gestfich.cpp:505
void CollectSubdirsLoopSafe(const wxString &aRoot, wxArrayString &aDirs, int aFlags)
Recursively collect every subdirectory under aRoot using the same loop detection as CollectFilesLoopS...
Definition gestfich.cpp:827
void CollectFilesLoopSafe(const wxString &aRoot, wxArrayString &aFiles, const wxString &aFileSpec, int aFlags)
Recursively collect every file under aRoot, deduplicating subdirectories by their resolved path.
Definition gestfich.cpp:817
static void traverseSEXPR(SEXPR::SEXPR *aNode, const std::function< void(SEXPR::SEXPR *)> &aVisitor)
Definition gestfich.cpp:322
bool CopyDirectory(const wxString &aSourceDir, const wxString &aDestDir, const std::vector< wxString > &aPathsWithOverwriteDisallowed, wxString &aErrors)
Copy a directory and its contents to another directory.
Definition gestfich.cpp:451
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:628
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
std::string path