You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
6.4 KiB

5 years ago
3 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 Wayne Stambaugh <stambaughw@gmail.com>
  5. * Copyright (C) 2017-2022 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. #include <env_paths.h>
  23. #include <project.h>
  24. #include <wx/filename.h>
  25. static bool normalizeAbsolutePaths( const wxFileName& aPathA, const wxFileName& aPathB,
  26. wxString* aResultPath )
  27. {
  28. wxCHECK_MSG( aPathA.IsAbsolute(), false, aPathA.GetPath() + wxS( " is not an absolute path." ) );
  29. wxCHECK_MSG( aPathB.IsAbsolute(), false, aPathB.GetPath() + wxS( " is not an absolute path." ) );
  30. if( aPathA.GetPath() == aPathB.GetPath() )
  31. return true;
  32. // Not sure all of volume checks are necessary since wxFileName::GetVolume() returns
  33. // an empty string if the path has no volume.
  34. if( ( aPathA.GetDirCount() > aPathB.GetDirCount() )
  35. || ( aPathA.HasVolume() && !aPathB.HasVolume() )
  36. || ( !aPathA.HasVolume() && aPathB.HasVolume() )
  37. || ( ( aPathA.HasVolume() && aPathB.HasVolume() )
  38. && ( aPathA.GetVolume().CmpNoCase( aPathB.GetVolume() ) != 0 ) ) )
  39. return false;
  40. wxArrayString aDirs = aPathA.GetDirs();
  41. wxArrayString bDirs = aPathB.GetDirs();
  42. size_t i = 0;
  43. while( i < aDirs.GetCount() )
  44. {
  45. if( aDirs[i] != bDirs[i] )
  46. return false;
  47. i++;
  48. }
  49. if( aResultPath )
  50. {
  51. while( i < bDirs.GetCount() )
  52. {
  53. *aResultPath += bDirs[i] + wxT( "/" );
  54. i++;
  55. }
  56. }
  57. return true;
  58. }
  59. wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
  60. const wxString& aProjectPath )
  61. {
  62. wxFileName envPath;
  63. wxString varName;
  64. wxString remainingPath;
  65. wxString normalizedFullPath;
  66. int pathDepth = 0;
  67. if( aEnvVars )
  68. {
  69. for( const std::pair<const wxString, ENV_VAR_ITEM>& entry : *aEnvVars )
  70. {
  71. // Don't bother normalizing paths that don't exist or the user cannot read.
  72. if( !wxFileName::DirExists( entry.second.GetValue() )
  73. || !wxFileName::IsDirReadable( entry.second.GetValue() ) )
  74. {
  75. continue;
  76. }
  77. envPath.SetPath( entry.second.GetValue() );
  78. wxString tmp;
  79. if( normalizeAbsolutePaths( envPath, aFilePath, &tmp ) )
  80. {
  81. int newDepth = envPath.GetDirs().GetCount();
  82. // Only use the variable if it removes more directories than the previous ones
  83. if( newDepth > pathDepth )
  84. {
  85. pathDepth = newDepth;
  86. varName = entry.first;
  87. remainingPath = tmp;
  88. }
  89. // @fixme Shouldn't we break here if an environment variable path is found or try
  90. // at least try to pick the best match?
  91. }
  92. }
  93. }
  94. if( varName.IsEmpty() && !aProjectPath.IsEmpty()
  95. && wxFileName( aProjectPath ).IsAbsolute() && wxFileName( aFilePath ).IsAbsolute() )
  96. {
  97. envPath.SetPath( aProjectPath );
  98. if( normalizeAbsolutePaths( envPath, aFilePath, &remainingPath ) )
  99. varName = PROJECT_VAR_NAME;
  100. }
  101. if( varName.IsEmpty() )
  102. {
  103. normalizedFullPath = aFilePath.GetFullPath();
  104. }
  105. else
  106. {
  107. normalizedFullPath = wxString::Format( "${%s}/", varName );
  108. if( !remainingPath.IsEmpty() )
  109. normalizedFullPath += remainingPath;
  110. normalizedFullPath += aFilePath.GetFullName();
  111. }
  112. return normalizedFullPath;
  113. }
  114. wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars,
  115. const PROJECT* aProject )
  116. {
  117. if( aProject )
  118. return NormalizePath( aFilePath, aEnvVars, aProject->GetProjectPath() );
  119. else
  120. return NormalizePath( aFilePath, aEnvVars, "" );
  121. }
  122. // Create file path by appending path and file name. This approach allows the filename
  123. // to contain a relative path, whereas wxFileName::SetPath() would replace the
  124. // relative path
  125. static wxString createFilePath( const wxString& aPath, const wxString& aFileName )
  126. {
  127. wxString path( aPath );
  128. if( !path.EndsWith( wxFileName::GetPathSeparator() ) )
  129. path.Append( wxFileName::GetPathSeparator() );
  130. return path + aFileName;
  131. }
  132. wxString ResolveFile( const wxString& aFileName, const ENV_VAR_MAP* aEnvVars,
  133. const PROJECT* aProject )
  134. {
  135. wxFileName full( aFileName );
  136. if( full.IsAbsolute() )
  137. return full.GetFullPath();
  138. if( aProject )
  139. {
  140. wxFileName fn( createFilePath( aProject->GetProjectPath(), aFileName ) );
  141. if( fn.Exists() )
  142. return fn.GetFullPath();
  143. }
  144. if( aEnvVars )
  145. {
  146. for( const std::pair<const wxString, ENV_VAR_ITEM>& entry : *aEnvVars )
  147. {
  148. wxFileName fn( createFilePath( entry.second.GetValue(), aFileName ) );
  149. if( fn.Exists() )
  150. return fn.GetFullPath();
  151. }
  152. }
  153. return wxEmptyString;
  154. }
  155. bool PathIsInsideProject( const wxString& aFileName, const PROJECT* aProject, wxFileName* aSubPath )
  156. {
  157. wxFileName fn( aFileName );
  158. wxFileName prj( aProject->GetProjectPath() );
  159. wxArrayString pdirs = prj.GetDirs();
  160. wxArrayString fdirs = fn.GetDirs();
  161. if( fdirs.size() < pdirs.size() )
  162. return false;
  163. for( size_t i = 0; i < pdirs.size(); i++ )
  164. {
  165. if( fdirs[i] != pdirs[i] )
  166. return false;
  167. }
  168. // Now we know that fn is inside prj
  169. if( aSubPath )
  170. {
  171. aSubPath->Clear();
  172. for( size_t i = pdirs.size(); i < fdirs.size(); i++ )
  173. aSubPath->AppendDir( fdirs[i] );
  174. }
  175. return true;
  176. }