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