KiCad PCB EDA Suite
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 (C) 2017-2022 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 along
20 * with this program. If not, see <http://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, aPathA.GetPath() + " is not an absolute path." );
31 wxCHECK_MSG( aPathB.IsAbsolute(), false, aPathB.GetPath() + " is not an absolute path." );
32
33 if( aPathA.GetPath() == aPathB.GetPath() )
34 return true;
35
36 // Not sure all of volume checks are necessary since wxFileName::GetVolume() returns
37 // an empty string if the path has no volume.
38 if( ( aPathA.GetDirCount() > aPathB.GetDirCount() )
39 || ( aPathA.HasVolume() && !aPathB.HasVolume() )
40 || ( !aPathA.HasVolume() && aPathB.HasVolume() )
41 || ( ( aPathA.HasVolume() && aPathB.HasVolume() )
42 && ( aPathA.GetVolume().CmpNoCase( aPathB.GetVolume() ) != 0 ) ) )
43 return false;
44
45 wxArrayString aDirs = aPathA.GetDirs();
46 wxArrayString bDirs = aPathB.GetDirs();
47
48 size_t i = 0;
49
50 while( i < aDirs.GetCount() )
51 {
52 if( aDirs[i] != bDirs[i] )
53 return false;
54
55 i++;
56 }
57
58 if( aResultPath )
59 {
60 while( i < bDirs.GetCount() )
61 {
62 *aResultPath += bDirs[i] + wxT( "/" );
63 i++;
64 }
65 }
66
67 return true;
68}
69
70
71wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
72 const wxString& aProjectPath )
73{
74 wxFileName envPath;
75 wxString varName;
76 wxString remainingPath;
77 wxString normalizedFullPath;
78 int pathDepth = 0;
79
80 if( aEnvVars )
81 {
82 for( const std::pair<const wxString, ENV_VAR_ITEM>& entry : *aEnvVars )
83 {
84 // Don't bother normalizing paths that don't exist or the user cannot read.
85 if( !wxFileName::DirExists( entry.second.GetValue() )
86 || !wxFileName::IsDirReadable( entry.second.GetValue() ) )
87 {
88 continue;
89 }
90
91 envPath.SetPath( entry.second.GetValue() );
92
93 wxString tmp;
94
95 if( normalizeAbsolutePaths( envPath, aFilePath, &tmp ) )
96 {
97 int newDepth = envPath.GetDirs().GetCount();
98
99 // Only use the variable if it removes more directories than the previous ones
100 if( newDepth > pathDepth )
101 {
102 pathDepth = newDepth;
103 varName = entry.first;
104 remainingPath = tmp;
105 }
106
107 // @fixme Shouldn't we break here if an environment variable path is found or try
108 // at least try to pick the best match?
109 }
110 }
111 }
112
113 if( varName.IsEmpty() && !aProjectPath.IsEmpty()
114 && wxFileName( aProjectPath ).IsAbsolute() && wxFileName( aFilePath ).IsAbsolute() )
115 {
116 envPath.SetPath( aProjectPath );
117
118 if( normalizeAbsolutePaths( envPath, aFilePath, &remainingPath ) )
119 varName = PROJECT_VAR_NAME;
120 }
121
122 if( varName.IsEmpty() )
123 {
124 normalizedFullPath = aFilePath.GetFullPath();
125 }
126 else
127 {
128 normalizedFullPath = wxString::Format( "${%s}/", varName );
129
130 if( !remainingPath.IsEmpty() )
131 normalizedFullPath += remainingPath;
132
133 normalizedFullPath += aFilePath.GetFullName();
134 }
135
136 return normalizedFullPath;
137}
138
139
140wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
141 const PROJECT* aProject )
142{
143 if( aProject )
144 return NormalizePath( aFilePath, aEnvVars, aProject->GetProjectPath() );
145 else
146 return NormalizePath( aFilePath, aEnvVars, "" );
147}
148
149
150// Create file path by appending path and file name. This approach allows the filename
151// to contain a relative path, whereas wxFileName::SetPath() would replace the
152// relative path
153static wxString createFilePath( const wxString& aPath, const wxString& aFileName )
154{
155 wxString path( aPath );
156
157 if( !path.EndsWith( wxFileName::GetPathSeparator() ) )
158 path.Append( wxFileName::GetPathSeparator() );
159
160 return path + aFileName;
161}
162
163
164wxString ResolveFile( const wxString& aFileName, const ENV_VAR_MAP* aEnvVars,
165 const PROJECT* aProject )
166{
167 wxFileName full( aFileName );
168
169 if( full.IsAbsolute() )
170 return full.GetFullPath();
171
172 if( aProject )
173 {
174 wxFileName fn( createFilePath( aProject->GetProjectPath(), aFileName ) );
175
176 if( fn.Exists() )
177 return fn.GetFullPath();
178 }
179
180 if( aEnvVars )
181 {
182 for( const std::pair<const wxString, ENV_VAR_ITEM>& entry : *aEnvVars )
183 {
184 wxFileName fn( createFilePath( entry.second.GetValue(), aFileName ) );
185
186 if( fn.Exists() )
187 return fn.GetFullPath();
188 }
189 }
190
191 return wxEmptyString;
192}
193
194
195bool PathIsInsideProject( const wxString& aFileName, const PROJECT* aProject, wxFileName* aSubPath )
196{
197 wxFileName fn( aFileName );
198 wxFileName prj( aProject->GetProjectPath() );
199
200 wxArrayString pdirs = prj.GetDirs();
201 wxArrayString fdirs = fn.GetDirs();
202
203 if( fdirs.size() < pdirs.size() )
204 return false;
205
206 for( size_t i = 0; i < pdirs.size(); i++ )
207 {
208 if( fdirs[i] != pdirs[i] )
209 return false;
210 }
211
212 // Now we know that fn is inside prj
213 if( aSubPath )
214 {
215 aSubPath->Clear();
216
217 for( size_t i = pdirs.size(); i < fdirs.size(); i++ )
218 aSubPath->AppendDir( fdirs[i] );
219 }
220
221 return true;
222}
Container for project specific data.
Definition: project.h:63
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:125
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:71
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.
Definition: env_paths.cpp:164
static wxString createFilePath(const wxString &aPath, const wxString &aFileName)
Definition: env_paths.cpp:153
bool PathIsInsideProject(const wxString &aFileName, const PROJECT *aProject, wxFileName *aSubPath)
Definition: env_paths.cpp:195
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:38
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200