KiCad PCB EDA Suite
Loading...
Searching...
No Matches
downgrade_copy.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7#pragma once
8
9#include <filesystem>
10#include <system_error>
11#include <vector>
12#include <wx/filename.h>
13#include <wx/intl.h>
14#include <downgrade_scan.h>
15
16inline std::filesystem::path DowngradeNativePath( const wxString& aPath )
17{
18#ifdef _WIN32
19 return std::filesystem::path( aPath.ToStdWstring() );
20#else
21 return std::filesystem::path( aPath.utf8_string() );
22#endif
23}
24
25inline wxString DowngradeWxPath( const std::filesystem::path& aPath )
26{
27#ifdef _WIN32
28 return wxString( aPath.wstring() );
29#else
30 return wxString::FromUTF8( aPath.string() );
31#endif
32}
33
34inline bool ValidateFreshDowngradeOutput( const wxString& aOutput, wxString& aError )
35{
36 if( aOutput.IsEmpty() )
37 {
38 aError = _( "An output path is required. Use --output." );
39 return false;
40 }
41
42 wxFileName output( aOutput );
43
44 // Match the normalization used by transaction destinations before testing freshness.
45 if( !output.MakeAbsolute() )
46 {
47 aError = _( "The output path is not accessible." );
48 return false;
49 }
50
51 std::error_code error;
52 auto status = std::filesystem::symlink_status( DowngradeNativePath( output.GetFullPath() ), error );
53
54 if( ( error && error != std::errc::no_such_file_or_directory )
55 || status.type() != std::filesystem::file_type::not_found )
56 {
57 aError = _( "The output path already exists or is inaccessible. Choose a new destination." );
58 return false;
59 }
60
61 return true;
62}
63
64inline bool IsDowngradeLibraryWorkingArtifact( const wxString& aPath )
65{
66 wxFileName file( aPath );
67 return IsDowngradeWorkingArtifact( aPath ) || file.GetExt() == wxT( "lck" ) || file.GetExt() == wxT( "kicad_prl" )
68 || file.GetFullName().Contains( wxT( ".downgrade_tmp." ) )
69 || file.GetFullName().Contains( wxT( ".downgrade_backup." ) );
70}
71
72inline bool ValidateDowngradeLibrarySource( const wxString& aSource, wxString& aError )
73{
74 std::error_code error;
75 auto source = std::filesystem::canonical( DowngradeNativePath( aSource ), error );
76
77 if( error || !std::filesystem::is_directory( source, error ) || error )
78 {
79 aError = _( "The library folder is not accessible." );
80 return false;
81 }
82
83 std::filesystem::recursive_directory_iterator iterator( source, error );
84 const std::filesystem::recursive_directory_iterator end;
85
86 while( !error && iterator != end )
87 {
88 const auto& entry = *iterator;
89 wxString path = DowngradeWxPath( entry.path() );
90
92 {
93 iterator.disable_recursion_pending();
94 }
95 else if( entry.is_symlink( error ) && entry.is_directory( error ) )
96 {
97 aError = _( "Library folders containing directory links cannot be copied safely." );
98 return false;
99 }
100 else if( !error && !entry.is_directory( error ) && !entry.is_regular_file( error ) )
101 {
102 aError = _( "The library folder contains an inaccessible or unsupported file." );
103 return false;
104 }
105
106 if( !error )
107 iterator.increment( error );
108 }
109
110 if( error )
111 {
112 aError = _( "The library folder could not be read completely." );
113 return false;
114 }
115
116 return true;
117}
118
119inline bool ValidateDowngradeLibraryCopy( const wxString& aSource, const wxString& aDestination, wxString& aError )
120{
121 if( !ValidateFreshDowngradeOutput( aDestination, aError ) || !ValidateDowngradeLibrarySource( aSource, aError ) )
122 {
123 return false;
124 }
125
126 std::error_code error;
127 auto source = std::filesystem::canonical( DowngradeNativePath( aSource ), error );
128 auto destination = std::filesystem::weakly_canonical( DowngradeNativePath( aDestination ), error );
129
130 if( error )
131 {
132 aError = _( "The output path is not accessible." );
133 return false;
134 }
135
136 auto relative = destination.lexically_relative( source );
137
138 if( destination == source || ( !relative.empty() && *relative.begin() != ".." ) )
139 {
140 aError = _( "The library output must be outside the source library folder." );
141 return false;
142 }
143
144 return true;
145}
146
148{
149public:
153
155 {
156 if( m_committed )
157 return;
158
159 for( auto it = m_created.rbegin(); it != m_created.rend(); ++it )
160 {
161 std::error_code error;
162
163 if( it->second )
164 std::filesystem::remove_all( it->first, error );
165 else
166 std::filesystem::remove( it->first, error );
167 }
168 }
169
170 bool Create( const wxString& aDirectory, bool aOwnContents = false )
171 {
172 std::error_code error;
173 auto directory = std::filesystem::absolute( DowngradeNativePath( aDirectory ), error ).lexically_normal();
174
175 if( error )
176 return false;
177
178 std::vector<std::filesystem::path> missing;
179
180 for( auto path = directory; !std::filesystem::exists( path, error ); path = path.parent_path() )
181 {
182 if( error || path.empty() || path == path.parent_path() )
183 return false;
184
185 missing.push_back( path );
186 }
187
188 if( error || ( aOwnContents && missing.empty() ) )
189 return false;
190
191 for( auto it = missing.rbegin(); it != missing.rend(); ++it )
192 {
193 if( !std::filesystem::create_directory( *it, error ) || error )
194 return false;
195
196 m_created.emplace_back( *it, aOwnContents && *it == directory );
197 }
198
199 return std::filesystem::is_directory( directory, error ) && !error;
200 }
201
202 void Commit() { m_committed = true; }
203
204private:
205 std::vector<std::pair<std::filesystem::path, bool>> m_created;
206 bool m_committed = false;
207};
208
209inline wxString DowngradeLibraryCopyPath( const wxString& aLibrary, const wxString& aOutputDirectory )
210{
211 std::error_code error;
212 auto source = std::filesystem::canonical( DowngradeNativePath( aLibrary ), error );
213
214 if( error )
215 return wxEmptyString;
216
217 return DowngradeWxPath( DowngradeNativePath( aOutputDirectory ) / source.filename() );
218}
219
220inline bool CopyDowngradeLibraryDirectory( const wxString& aSource, const wxString& aDestination,
221 DOWNGRADE_OUTPUT_DIRECTORIES& aDirectories, wxString& aError )
222{
223 if( !ValidateDowngradeLibraryCopy( aSource, aDestination, aError ) )
224 return false;
225
226 if( !aDirectories.Create( aDestination, true ) )
227 {
228 aError = _( "Could not create the library output folder." );
229 return false;
230 }
231
232 std::error_code error;
233 auto source = std::filesystem::canonical( DowngradeNativePath( aSource ), error );
234 std::filesystem::recursive_directory_iterator iterator( source, error );
235 const std::filesystem::recursive_directory_iterator end;
236
237 while( !error && iterator != end )
238 {
239 const auto& entry = *iterator;
240
242 {
243 iterator.disable_recursion_pending();
244 }
245 else
246 {
247 auto destination = DowngradeNativePath( aDestination ) / entry.path().lexically_relative( source );
248
249 if( entry.is_directory( error ) )
250 std::filesystem::create_directory( destination, error );
251 else if( !error )
252 std::filesystem::copy_file( entry.path(), destination, error );
253 }
254
255 if( !error )
256 iterator.increment( error );
257 }
258
259 if( error )
260 {
261 aError = _( "Could not copy the library folder." );
262 return false;
263 }
264
265 return true;
266}
std::vector< std::pair< std::filesystem::path, bool > > m_created
DOWNGRADE_OUTPUT_DIRECTORIES(const DOWNGRADE_OUTPUT_DIRECTORIES &)=delete
DOWNGRADE_OUTPUT_DIRECTORIES & operator=(const DOWNGRADE_OUTPUT_DIRECTORIES &)=delete
bool Create(const wxString &aDirectory, bool aOwnContents=false)
wxString DowngradeWxPath(const std::filesystem::path &aPath)
wxString DowngradeLibraryCopyPath(const wxString &aLibrary, const wxString &aOutputDirectory)
bool IsDowngradeLibraryWorkingArtifact(const wxString &aPath)
bool ValidateDowngradeLibrarySource(const wxString &aSource, wxString &aError)
bool ValidateDowngradeLibraryCopy(const wxString &aSource, const wxString &aDestination, wxString &aError)
bool ValidateFreshDowngradeOutput(const wxString &aOutput, wxString &aError)
bool CopyDowngradeLibraryDirectory(const wxString &aSource, const wxString &aDestination, DOWNGRADE_OUTPUT_DIRECTORIES &aDirectories, wxString &aError)
std::filesystem::path DowngradeNativePath(const wxString &aPath)
bool IsDowngradeWorkingArtifact(const wxString &aPath)
Editor working state (autosaves, local history, backups) holds current-format copies nothing downgrad...
#define _(s)
std::string source
std::string path
VECTOR2I end