KiCad PCB EDA Suite
Loading...
Searching...
No Matches
env_paths.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) 2017 Wayne Stambaugh <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Copyright (C) 2017 CERN
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <env_paths.h>
24#include <project.h>
25#include <wx/filename.h>
26
27static bool normalizeAbsolutePaths( const wxFileName& aPathA, const wxFileName& aPathB,
28 wxString* aResultPath )
29{
30 wxCHECK_MSG( aPathA.IsAbsolute(), false,
31 aPathA.GetPath() + wxS( " is not an absolute path." ) );
32 wxCHECK_MSG( aPathB.IsAbsolute(), false,
33 aPathB.GetPath() + wxS( " is not an absolute path." ) );
34
35 if( aPathA.GetPath() == aPathB.GetPath() )
36 return true;
37
38 // Not sure all of volume checks are necessary since wxFileName::GetVolume() returns
39 // an empty string if the path has no volume.
40 if( ( aPathA.GetDirCount() > aPathB.GetDirCount() )
41 || ( aPathA.HasVolume() && !aPathB.HasVolume() )
42 || ( !aPathA.HasVolume() && aPathB.HasVolume() )
43 || ( ( aPathA.HasVolume() && aPathB.HasVolume() )
44 && ( aPathA.GetVolume().CmpNoCase( aPathB.GetVolume() ) != 0 ) ) )
45 return false;
46
47 wxArrayString aDirs = aPathA.GetDirs();
48 wxArrayString bDirs = aPathB.GetDirs();
49
50 size_t i = 0;
51
52 while( i < aDirs.GetCount() )
53 {
54 if( aDirs[i] != bDirs[i] )
55 return false;
56
57 i++;
58 }
59
60 if( aResultPath )
61 {
62 while( i < bDirs.GetCount() )
63 {
64 *aResultPath += bDirs[i] + wxT( "/" );
65 i++;
66 }
67 }
68
69 return true;
70}
71
72
73wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
74 const wxString& aProjectPath )
75{
76 wxFileName envPath;
77 wxString varName;
78 wxString remainingPath;
79 wxString normalizedFullPath;
80 int pathDepth = 0;
81
82 if( aEnvVars )
83 {
84 for( const std::pair<const wxString, ENV_VAR_ITEM>& entry : *aEnvVars )
85 {
86 // Don't bother normalizing paths that don't exist or the user cannot read.
87 if( !wxFileName::DirExists( entry.second.GetValue() )
88 || !wxFileName::IsDirReadable( entry.second.GetValue() ) )
89 {
90 continue;
91 }
92
93 envPath.SetPath( entry.second.GetValue() );
94
95 wxString tmp;
96
97 if( normalizeAbsolutePaths( envPath, aFilePath, &tmp ) )
98 {
99 int newDepth = envPath.GetDirs().GetCount();
100
101 // Only use the variable if it removes more directories than the previous ones
102 if( newDepth > pathDepth )
103 {
104 pathDepth = newDepth;
105 varName = entry.first;
106 remainingPath = tmp;
107 }
108
109 // @fixme Shouldn't we break here if an environment variable path is found or try
110 // at least try to pick the best match?
111 }
112 }
113 }
114
115 if( varName.IsEmpty() && !aProjectPath.IsEmpty()
116 && wxFileName( aProjectPath ).IsAbsolute() && wxFileName( aFilePath ).IsAbsolute() )
117 {
118 envPath.SetPath( aProjectPath );
119
120 if( normalizeAbsolutePaths( envPath, aFilePath, &remainingPath ) )
121 varName = PROJECT_VAR_NAME;
122 }
123
124 if( varName.IsEmpty() )
125 {
126 normalizedFullPath = aFilePath.GetFullPath();
127 }
128 else
129 {
130 normalizedFullPath = wxString::Format( "${%s}/", varName );
131
132 if( !remainingPath.IsEmpty() )
133 normalizedFullPath += remainingPath;
134
135 normalizedFullPath += aFilePath.GetFullName();
136 }
137
138 return normalizedFullPath;
139}
140
141
142wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
143 const PROJECT* aProject )
144{
145 if( aProject )
146 return NormalizePath( aFilePath, aEnvVars, aProject->GetProjectPath() );
147 else
148 return NormalizePath( aFilePath, aEnvVars, "" );
149}
150
151
152wxString MakeRelativePath( const wxString& aPath, const wxString& aBaseDir )
153{
154 wxFileName fn( aPath );
155 wxFileName base = wxFileName::DirName( aBaseDir );
156
157 if( aBaseDir.IsEmpty() || !fn.IsAbsolute() || !base.IsAbsolute() )
158 return aPath;
159
160 if( wxFileName::DirName( aPath ).SameAs( base ) )
161 return wxS( "." );
162
163 if( !fn.MakeRelativeTo( base.GetPath() ) )
164 return aPath;
165
166 return fn.GetFullPath( wxPATH_UNIX );
167}
168
169
170wxString NormalizePathForProject( const wxString& aPath, const ENV_VAR_MAP* aEnvVars,
171 const wxString& aProjectPath, bool aAllowParentDirs )
172{
173 wxFileName fn( aPath );
174
175 if( !fn.IsAbsolute() )
176 return aPath;
177
178 wxString relPath = MakeRelativePath( aPath, aProjectPath );
179
180 if( relPath != aPath && relPath != wxS( ".." ) && !relPath.StartsWith( wxS( "../" ) ) )
181 return relPath;
182
183 // A "../" path only survives moving the project together with its surroundings
184 wxString envPath = NormalizePath( fn, aEnvVars, wxEmptyString );
185
186 if( envPath != fn.GetFullPath() || !aAllowParentDirs )
187 return envPath;
188
189 return relPath;
190}
191
192
193// Create file path by appending path and file name. This approach allows the filename
194// to contain a relative path, whereas wxFileName::SetPath() would replace the
195// relative path
196static wxString createFilePath( const wxString& aPath, const wxString& aFileName )
197{
198 wxString path( aPath );
199
200 if( !path.EndsWith( wxFileName::GetPathSeparator() ) )
201 path.Append( wxFileName::GetPathSeparator() );
202
203 return path + aFileName;
204}
205
206
207wxString ResolveFile( const wxString& aFileName, const ENV_VAR_MAP* aEnvVars,
208 const PROJECT* aProject )
209{
210 wxFileName full( aFileName );
211
212 if( full.IsAbsolute() )
213 return full.GetFullPath();
214
215 if( aProject )
216 {
217 wxFileName fn( createFilePath( aProject->GetProjectPath(), aFileName ) );
218
219 if( fn.Exists() )
220 return fn.GetFullPath();
221 }
222
223 if( aEnvVars )
224 {
225 for( const std::pair<const wxString, ENV_VAR_ITEM>& entry : *aEnvVars )
226 {
227 wxFileName fn( createFilePath( entry.second.GetValue(), aFileName ) );
228
229 if( fn.Exists() )
230 return fn.GetFullPath();
231 }
232 }
233
234 return wxEmptyString;
235}
236
237
238bool PathIsInsideProject( const wxString& aFileName, const PROJECT* aProject, wxFileName* aSubPath )
239{
240 wxFileName fn( aFileName );
241 wxFileName prj( aProject->GetProjectPath() );
242
243 wxArrayString pdirs = prj.GetDirs();
244 wxArrayString fdirs = fn.GetDirs();
245
246 if( fdirs.size() < pdirs.size() )
247 return false;
248
249 for( size_t i = 0; i < pdirs.size(); i++ )
250 {
251 if( fdirs[i] != pdirs[i] )
252 return false;
253 }
254
255 // Now we know that fn is inside prj
256 if( aSubPath )
257 {
258 aSubPath->Clear();
259
260 for( size_t i = pdirs.size(); i < fdirs.size(); i++ )
261 aSubPath->AppendDir( fdirs[i] );
262 }
263
264 return true;
265}
Container for project specific data.
Definition project.h:63
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:276
static bool normalizeAbsolutePaths(const wxFileName &aPathA, const wxFileName &aPathB, wxString *aResultPath)
Definition env_paths.cpp:27
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalize a file path to an environmental variable, if possible.
Definition env_paths.cpp:73
wxString ResolveFile(const wxString &aFileName, const ENV_VAR_MAP *aEnvVars, const PROJECT *aProject)
Search the default paths trying to find one with the requested file.
wxString MakeRelativePath(const wxString &aPath, const wxString &aBaseDir)
Express an absolute path relative to a base directory, stepping out with ".." when needed.
wxString NormalizePathForProject(const wxString &aPath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath, bool aAllowParentDirs)
Choose how to store a path the user picked for a project.
static wxString createFilePath(const wxString &aPath, const wxString &aFileName)
bool PathIsInsideProject(const wxString &aFileName, const PROJECT *aProject, wxFileName *aSubPath)
Helper functions to substitute paths with environmental variables.
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition project.h:37
std::string path