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.

282 lines
6.2 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 2010-2020 KiCad Developers, see change_log.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <cstring>
  26. #include <memory>
  27. #include <wx/translation.h>
  28. #include <macros.h> // TO_UTF8()
  29. #include <lib_id.h>
  30. static inline int okLogical( const UTF8& aField )
  31. {
  32. // std::string::npos is largest positive number, casting to int makes it -1.
  33. // Returning that means success.
  34. return int( aField.find_first_of( ":" ) );
  35. }
  36. void LIB_ID::clear()
  37. {
  38. m_libraryName.clear();
  39. m_itemName.clear();
  40. m_subLibraryName.clear();
  41. }
  42. int LIB_ID::Parse( const UTF8& aId, bool aFix )
  43. {
  44. clear();
  45. size_t partNdx;
  46. int offset = -1;
  47. //=====<library nickname>=============================
  48. if( ( partNdx = aId.find( ':' ) ) != aId.npos )
  49. {
  50. offset = SetLibNickname( aId.substr( 0, partNdx ) );
  51. if( offset > -1 )
  52. return offset;
  53. ++partNdx; // skip ':'
  54. }
  55. else
  56. {
  57. partNdx = 0;
  58. }
  59. //=====<item name>====================================
  60. UTF8 fpname = aId.substr( partNdx );
  61. // Be sure the item name is valid.
  62. // Some chars can be found in legacy files converted files from other EDA tools.
  63. if( aFix )
  64. fpname = FixIllegalChars( fpname, false );
  65. else
  66. offset = HasIllegalChars( fpname );
  67. if( offset > -1 )
  68. return offset;
  69. SetLibItemName( fpname );
  70. return -1;
  71. }
  72. LIB_ID::LIB_ID( const wxString& aLibraryName, const wxString& aItemName ) :
  73. m_libraryName( aLibraryName ),
  74. m_itemName( aItemName )
  75. {
  76. }
  77. int LIB_ID::SetLibNickname( const UTF8& aLogical )
  78. {
  79. int offset = okLogical( aLogical );
  80. if( offset == -1 )
  81. m_libraryName = aLogical;
  82. return offset;
  83. }
  84. int LIB_ID::SetLibItemName( const UTF8& aLibItemName )
  85. {
  86. m_itemName = aLibItemName;
  87. return -1;
  88. }
  89. UTF8 LIB_ID::Format() const
  90. {
  91. UTF8 ret;
  92. if( m_libraryName.size() )
  93. {
  94. ret += m_libraryName;
  95. ret += ':';
  96. }
  97. ret += m_itemName;
  98. return ret;
  99. }
  100. UTF8 LIB_ID::Format( const UTF8& aLibraryName, const UTF8& aLibItemName )
  101. {
  102. UTF8 ret;
  103. int offset;
  104. if( aLibraryName.size() )
  105. {
  106. offset = okLogical( aLibraryName );
  107. if( offset != -1 )
  108. {
  109. THROW_PARSE_ERROR( _( "Illegal character found in logical library name" ),
  110. wxString::FromUTF8( aLibraryName.c_str() ), aLibraryName.c_str(),
  111. 0, offset );
  112. }
  113. ret += aLibraryName;
  114. ret += ':';
  115. }
  116. ret += aLibItemName; // TODO: Add validity test.
  117. return ret;
  118. }
  119. int LIB_ID::compare( const LIB_ID& aLibId ) const
  120. {
  121. // Don't bother comparing the same object.
  122. if( this == &aLibId )
  123. return 0;
  124. int retv = m_libraryName.compare( aLibId.m_libraryName );
  125. if( retv != 0 )
  126. return retv;
  127. return m_itemName.compare( aLibId.m_itemName );
  128. }
  129. int LIB_ID::HasIllegalChars( const UTF8& aLibItemName )
  130. {
  131. int offset = 0;
  132. for( auto& ch : aLibItemName )
  133. {
  134. if( !isLegalChar( ch ) )
  135. return offset;
  136. else
  137. ++offset;
  138. }
  139. return -1;
  140. }
  141. UTF8 LIB_ID::FixIllegalChars( const UTF8& aLibItemName, bool aLib )
  142. {
  143. UTF8 fixedName;
  144. for( UTF8::uni_iter chIt = aLibItemName.ubegin(); chIt < aLibItemName.uend(); ++chIt )
  145. {
  146. auto ch = *chIt;
  147. if( aLib )
  148. fixedName += isLegalLibraryNameChar( ch ) ? ch : '_';
  149. else
  150. fixedName += isLegalChar( ch ) ? ch : '_';
  151. }
  152. return fixedName;
  153. }
  154. bool LIB_ID::isLegalChar( unsigned aUniChar )
  155. {
  156. bool const space_allowed = true;
  157. bool const illegal_filename_chars_allowed = false;
  158. if( aUniChar < ' ' )
  159. return false;
  160. // This list of characters is also duplicated in validators.cpp and footprint.cpp
  161. // TODO: Unify forbidden character lists - Warning, invalid filename characters are not the same
  162. // as invalid LIB_ID characters. We will need to separate the FP filenames from FP names before this
  163. // can be unified
  164. switch( aUniChar )
  165. {
  166. case ':':
  167. case '\t':
  168. case '\n':
  169. case '\r':
  170. return false;
  171. case '\\':
  172. case '<':
  173. case '>':
  174. case '"':
  175. return illegal_filename_chars_allowed;
  176. case ' ':
  177. return space_allowed;
  178. default:
  179. return true;
  180. }
  181. }
  182. unsigned LIB_ID::FindIllegalLibraryNameChar( const UTF8& aLibraryName )
  183. {
  184. for( unsigned ch : aLibraryName )
  185. {
  186. if( !isLegalLibraryNameChar( ch ) )
  187. return ch;
  188. }
  189. return 0;
  190. }
  191. bool LIB_ID::isLegalLibraryNameChar( unsigned aUniChar )
  192. {
  193. bool const space_allowed = true;
  194. if( aUniChar < ' ' )
  195. return false;
  196. switch( aUniChar )
  197. {
  198. case '\\':
  199. case ':':
  200. return false;
  201. case ' ':
  202. return space_allowed;
  203. default:
  204. return true;
  205. }
  206. }
  207. const wxString LIB_ID::GetFullLibraryName() const
  208. {
  209. wxString suffix = m_subLibraryName.wx_str().IsEmpty()
  210. ? wxString( wxS( "" ) )
  211. : wxString::Format( wxT( " - %s" ), m_subLibraryName.wx_str() );
  212. return wxString::Format( wxT( "%s%s" ), m_libraryName.wx_str(), suffix );
  213. }