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