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 <stambaughw@verizon.net>
5  * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
6  * Copyright (C) 2017 CERN
7  * @author Maciej Suminski <maciej.suminski@cern.ch>
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 
25 static bool normalizeAbsolutePaths( const wxFileName& aPathA,
26  const wxFileName& aPathB,
27  wxString* aResultPath )
28 {
29  wxCHECK_MSG( aPathA.IsAbsolute(), false, aPathA.GetPath() + " is not an absolute path." );
30  wxCHECK_MSG( aPathB.IsAbsolute(), false, aPathB.GetPath() + " is not an absolute path." );
31 
32  if( aPathA.GetPath() == aPathB.GetPath() )
33  return true;
34 
35  if( ( aPathA.GetDirCount() > aPathB.GetDirCount() )
36  || ( aPathA.HasVolume() && !aPathB.HasVolume() )
37  || ( !aPathA.HasVolume() && aPathB.HasVolume() )
38  || ( ( aPathA.HasVolume() && aPathB.HasVolume() )
39  && ( aPathA.GetVolume() != aPathB.GetVolume() ) ) )
40  return false;
41 
42  wxArrayString aDirs = aPathA.GetDirs();
43  wxArrayString bDirs = aPathB.GetDirs();
44 
45  size_t i = 0;
46 
47  while( i < aDirs.GetCount() )
48  {
49  if( aDirs[i] != bDirs[i] )
50  return false;
51 
52  i++;
53  }
54 
55  if( aResultPath )
56  {
57  while( i < bDirs.GetCount() )
58  {
59  *aResultPath += bDirs[i] + wxT( "/" );
60  i++;
61  }
62  }
63 
64  return true;
65 }
66 
67 
68 wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
69  const wxString& aProjectPath )
70 {
71  wxFileName envPath;
72  wxString varName;
73  wxString remainingPath;
74  wxString normalizedFullPath;
75  int pathDepth = 0;
76 
77  if( aEnvVars )
78  {
79  for( auto& entry : *aEnvVars )
80  {
81  // Don't bother normalizing paths that don't exist or the user cannot read.
82  if( !wxFileName::DirExists( entry.second.GetValue() )
83  || !wxFileName::IsDirReadable( entry.second.GetValue() ) )
84  continue;
85 
86  envPath.SetPath( entry.second.GetValue() );
87 
88  wxString tmp;
89  if( normalizeAbsolutePaths( envPath, aFilePath, &tmp ) )
90  {
91  int newDepth = envPath.GetDirs().GetCount();
92 
93  // Only use the variable if it removes more directories than the previous ones
94  if( newDepth > pathDepth )
95  {
96  pathDepth = newDepth;
97  varName = entry.first;
98  remainingPath = tmp;
99  }
100  }
101  }
102  }
103 
104  if( varName.IsEmpty() && !aProjectPath.IsEmpty()
105  && wxFileName( aProjectPath ).IsAbsolute() && wxFileName( aFilePath ).IsAbsolute() )
106  {
107  envPath.SetPath( aProjectPath );
108 
109  if( normalizeAbsolutePaths( envPath, aFilePath, &remainingPath ) )
110  varName = PROJECT_VAR_NAME;
111  }
112 
113  if( !varName.IsEmpty() )
114  {
115  normalizedFullPath = wxString::Format( "${%s}/", varName );
116 
117  if( !remainingPath.IsEmpty() )
118  normalizedFullPath += remainingPath;
119 
120  normalizedFullPath += aFilePath.GetFullName();
121  }
122 
123  return normalizedFullPath;
124 }
125 
126 
127 wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
128  const PROJECT* aProject )
129 {
130  if( aProject )
131  return NormalizePath( aFilePath, aEnvVars, aProject->GetProjectPath() );
132  else
133  return NormalizePath( aFilePath, aEnvVars, "" );
134 }
135 
136 
137 // Create file path by appending path and file name. This approach allows the filename
138 // to contain a relative path, whereas wxFileName::SetPath() would replace the
139 // relative path
140 static wxString createFilePath( const wxString& aPath, const wxString& aFileName )
141 {
142  wxString path( aPath );
143 
144  if( !path.EndsWith( wxFileName::GetPathSeparator() ) )
145  path.Append( wxFileName::GetPathSeparator() );
146 
147  return path + aFileName;
148 }
149 
150 
151 wxString ResolveFile( const wxString& aFileName, const ENV_VAR_MAP* aEnvVars,
152  const PROJECT* aProject )
153 {
154  wxFileName full( aFileName );
155 
156  if( full.IsAbsolute() )
157  return full.GetFullPath();
158 
159  if( aProject )
160  {
161  wxFileName fn( createFilePath( aProject->GetProjectPath(), aFileName ) );
162 
163  if( fn.Exists() )
164  return fn.GetFullPath();
165  }
166 
167  if( aEnvVars )
168  {
169  for( auto& entry : *aEnvVars )
170  {
171  wxFileName fn( createFilePath( entry.second.GetValue(), aFileName ) );
172 
173  if( fn.Exists() )
174  return fn.GetFullPath();
175  }
176  }
177 
178  return wxEmptyString;
179 }
180 
181 
182 bool PathIsInsideProject( const wxString& aFileName, const PROJECT* aProject, wxFileName* aSubPath )
183 {
184  wxFileName fn( aFileName );
185  wxFileName prj( aProject->GetProjectPath() );
186 
187  wxArrayString pdirs = prj.GetDirs();
188  wxArrayString fdirs = fn.GetDirs();
189 
190  if( fdirs.size() < pdirs.size() )
191  return false;
192 
193  for( size_t i = 0; i < pdirs.size(); i++ )
194  {
195  if( fdirs[i] != pdirs[i] )
196  return false;
197  }
198 
199  // Now we know that fn is inside prj
200  if( aSubPath )
201  {
202  aSubPath->Clear();
203 
204  for( size_t i = pdirs.size(); i < fdirs.size(); i++ )
205  aSubPath->AppendDir( fdirs[i] );
206  }
207 
208  return true;
209 }
wxString ResolveFile(const wxString &aFileName, const ENV_VAR_MAP *aEnvVars, const PROJECT *aProject)
Searches the default paths trying to find one with the requested file.
Definition: env_paths.cpp:151
PROJECT holds project specific data.
Definition: project.h:61
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:38
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:121
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
Definition: pgm_base.h:117
static wxString createFilePath(const wxString &aPath, const wxString &aFileName)
Definition: env_paths.cpp:140
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
bool PathIsInsideProject(const wxString &aFileName, const PROJECT *aProject, wxFileName *aSubPath)
Checks if a given filename is within a given project directory (not whether it exists!...
Definition: env_paths.cpp:182
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalizes a file path to an environmental variable, if possible.
Definition: env_paths.cpp:68
static bool normalizeAbsolutePaths(const wxFileName &aPathA, const wxFileName &aPathB, wxString *aResultPath)
Definition: env_paths.cpp:25