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.

272 lines
5.7 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. }
  41. int LIB_ID::Parse( const UTF8& aId, bool aFix )
  42. {
  43. clear();
  44. size_t partNdx;
  45. int offset = -1;
  46. //=====<library nickname>=============================
  47. if( ( partNdx = aId.find( ':' ) ) != aId.npos )
  48. {
  49. offset = SetLibNickname( aId.substr( 0, partNdx ) );
  50. if( offset > -1 )
  51. return offset;
  52. ++partNdx; // skip ':'
  53. }
  54. else
  55. {
  56. partNdx = 0;
  57. }
  58. //=====<item name>====================================
  59. UTF8 fpname = aId.substr( partNdx );
  60. // Be sure the item name is valid.
  61. // Some chars can be found in legacy files converted files from other EDA tools.
  62. if( aFix )
  63. fpname = FixIllegalChars( fpname, false );
  64. else
  65. offset = HasIllegalChars( fpname );
  66. if( offset > -1 )
  67. return offset;
  68. SetLibItemName( fpname );
  69. return -1;
  70. }
  71. LIB_ID::LIB_ID( const wxString& aLibraryName, const wxString& aItemName ) :
  72. m_libraryName( aLibraryName ),
  73. m_itemName( aItemName )
  74. {
  75. }
  76. int LIB_ID::SetLibNickname( const UTF8& aLogical )
  77. {
  78. int offset = okLogical( aLogical );
  79. if( offset == -1 )
  80. m_libraryName = aLogical;
  81. return offset;
  82. }
  83. int LIB_ID::SetLibItemName( const UTF8& aLibItemName )
  84. {
  85. m_itemName = aLibItemName;
  86. return -1;
  87. }
  88. UTF8 LIB_ID::Format() const
  89. {
  90. UTF8 ret;
  91. if( m_libraryName.size() )
  92. {
  93. ret += m_libraryName;
  94. ret += ':';
  95. }
  96. ret += m_itemName;
  97. return ret;
  98. }
  99. UTF8 LIB_ID::Format( const UTF8& aLibraryName, const UTF8& aLibItemName )
  100. {
  101. UTF8 ret;
  102. int offset;
  103. if( aLibraryName.size() )
  104. {
  105. offset = okLogical( aLibraryName );
  106. if( offset != -1 )
  107. {
  108. THROW_PARSE_ERROR( _( "Illegal character found in logical library name" ),
  109. wxString::FromUTF8( aLibraryName.c_str() ), aLibraryName.c_str(),
  110. 0, offset );
  111. }
  112. ret += aLibraryName;
  113. ret += ':';
  114. }
  115. ret += aLibItemName; // TODO: Add validity test.
  116. return ret;
  117. }
  118. int LIB_ID::compare( const LIB_ID& aLibId ) const
  119. {
  120. // Don't bother comparing the same object.
  121. if( this == &aLibId )
  122. return 0;
  123. int retv = m_libraryName.compare( aLibId.m_libraryName );
  124. if( retv != 0 )
  125. return retv;
  126. return m_itemName.compare( aLibId.m_itemName );
  127. }
  128. int LIB_ID::HasIllegalChars( const UTF8& aLibItemName )
  129. {
  130. int offset = 0;
  131. for( auto ch : aLibItemName )
  132. {
  133. if( !isLegalChar( ch ) )
  134. return offset;
  135. else
  136. ++offset;
  137. }
  138. return -1;
  139. }
  140. UTF8 LIB_ID::FixIllegalChars( const UTF8& aLibItemName, bool aLib )
  141. {
  142. UTF8 fixedName;
  143. for( UTF8::uni_iter chIt = aLibItemName.ubegin(); chIt < aLibItemName.uend(); ++chIt )
  144. {
  145. auto ch = *chIt;
  146. if( aLib )
  147. fixedName += isLegalLibraryNameChar( ch ) ? ch : '_';
  148. else
  149. fixedName += isLegalChar( ch ) ? ch : '_';
  150. }
  151. return fixedName;
  152. }
  153. bool LIB_ID::isLegalChar( unsigned aUniChar )
  154. {
  155. bool const space_allowed = true;
  156. bool const illegal_filename_chars_allowed = false;
  157. if( aUniChar < ' ' )
  158. return false;
  159. // This list of characters is also duplicated in validators.cpp and footprint.cpp
  160. // TODO: Unify forbidden character lists
  161. switch( aUniChar )
  162. {
  163. case ':':
  164. case '\t':
  165. case '\n':
  166. case '\r':
  167. return false;
  168. case '/':
  169. case '\\':
  170. case '<':
  171. case '>':
  172. case '"':
  173. return illegal_filename_chars_allowed;
  174. case ' ':
  175. return space_allowed;
  176. default:
  177. return true;
  178. }
  179. }
  180. unsigned LIB_ID::FindIllegalLibraryNameChar( const UTF8& aLibraryName )
  181. {
  182. for( unsigned ch : aLibraryName )
  183. {
  184. if( !isLegalLibraryNameChar( ch ) )
  185. return ch;
  186. }
  187. return 0;
  188. }
  189. bool LIB_ID::isLegalLibraryNameChar( unsigned aUniChar )
  190. {
  191. bool const space_allowed = true;
  192. if( aUniChar < ' ' )
  193. return false;
  194. switch( aUniChar )
  195. {
  196. case '\\':
  197. case ':':
  198. return false;
  199. case ' ':
  200. return space_allowed;
  201. default:
  202. return true;
  203. }
  204. }