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.

405 lines
8.7 KiB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  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@verizon.net>
  6. * Copyright (C) 2010 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/wx.h> // _()
  28. #include <macros.h> // TO_UTF8()
  29. #include <fpid.h>
  30. static inline bool isDigit( char c )
  31. {
  32. return c >= '0' && c <= '9';
  33. }
  34. const char* EndsWithRev( const char* start, const char* tail, char separator )
  35. {
  36. bool sawDigit = false;
  37. while( tail > start && isDigit( *--tail ) )
  38. {
  39. sawDigit = true;
  40. }
  41. // if sawDigit, tail points to the 'v' here.
  42. if( sawDigit && tail-3 >= start )
  43. {
  44. tail -= 3;
  45. if( tail[0]==separator && tail[1]=='r' && tail[2]=='e' && tail[3]=='v' )
  46. {
  47. return tail+1; // omit separator, return "revN[N..]"
  48. }
  49. }
  50. return 0;
  51. }
  52. int RevCmp( const char* s1, const char* s2 )
  53. {
  54. int r = strncmp( s1, s2, 3 );
  55. if( r || strlen(s1)<4 || strlen(s2)<4 )
  56. {
  57. return r;
  58. }
  59. int rnum1 = atoi( s1+3 );
  60. int rnum2 = atoi( s2+3 );
  61. return -(rnum1 - rnum2); // swap the sign, higher revs first
  62. }
  63. //----<Policy and field test functions>-------------------------------------
  64. static inline int okLogical( const std::string& aField )
  65. {
  66. // std::string::npos is largest positive number, casting to int makes it -1.
  67. // Returning that means success.
  68. return int( aField.find_first_of( ":" ) );
  69. }
  70. static int okRevision( const std::string& aField )
  71. {
  72. char rev[32]; // C string for speed
  73. if( aField.size() >= 4 )
  74. {
  75. strcpy( rev, "x/" );
  76. strcat( rev, aField.c_str() );
  77. if( EndsWithRev( rev, rev + strlen(rev), '/' ) == rev+2 )
  78. return -1; // success
  79. }
  80. return 0; // first character position "is in error", is best we can do.
  81. }
  82. //----</Policy and field test functions>-------------------------------------
  83. void FPID::clear()
  84. {
  85. nickname.clear();
  86. footprint.clear();
  87. revision.clear();
  88. }
  89. int FPID::Parse( const std::string& aId )
  90. {
  91. clear();
  92. size_t cnt = aId.length() + 1;
  93. char tmp[cnt]; // C string for speed
  94. std::strcpy( tmp, aId.c_str() );
  95. const char* rev = EndsWithRev( tmp, tmp+aId.length(), '/' );
  96. size_t revNdx;
  97. size_t partNdx;
  98. int offset;
  99. //=====<revision>=========================================
  100. if( rev )
  101. {
  102. revNdx = rev - aId.c_str();
  103. // no need to check revision, EndsWithRev did that.
  104. revision = aId.substr( revNdx );
  105. --revNdx; // back up to omit the '/' which precedes the rev
  106. }
  107. else
  108. {
  109. revNdx = aId.size();
  110. }
  111. //=====<nickname>==========================================
  112. if( ( partNdx = aId.find( ':' ) ) != aId.npos )
  113. {
  114. offset = SetLibNickname( aId.substr( 0, partNdx ) );
  115. if( offset > -1 )
  116. {
  117. return offset;
  118. }
  119. ++partNdx; // skip ':'
  120. }
  121. else
  122. {
  123. partNdx = 0;
  124. }
  125. //=====<footprint name>====================================
  126. if( partNdx >= revNdx )
  127. return partNdx;
  128. SetFootprintName( aId.substr( partNdx, revNdx ) );
  129. return -1;
  130. }
  131. int FPID::Parse( const wxString& aId )
  132. {
  133. return Parse( std::string( TO_UTF8( aId ) ) );
  134. }
  135. FPID::FPID( const std::string& aId ) throw( PARSE_ERROR )
  136. {
  137. int offset = Parse( aId );
  138. if( offset != -1 )
  139. {
  140. THROW_PARSE_ERROR( _( "Illegal character found in FPID string" ),
  141. wxString::FromUTF8( aId.c_str() ),
  142. aId.c_str(),
  143. 0,
  144. offset );
  145. }
  146. }
  147. FPID::FPID( const wxString& aId ) throw( PARSE_ERROR )
  148. {
  149. std::string id = TO_UTF8( aId );
  150. int offset = Parse( id );
  151. if( offset != -1 )
  152. {
  153. THROW_PARSE_ERROR( _( "Illegal character found in FPID string" ),
  154. wxString::FromUTF8( id.c_str() ),
  155. id.c_str(),
  156. 0,
  157. offset );
  158. }
  159. }
  160. int FPID::SetLibNickname( const std::string& aLogical )
  161. {
  162. int offset = okLogical( aLogical );
  163. if( offset == -1 )
  164. {
  165. nickname = aLogical;
  166. }
  167. return offset;
  168. }
  169. int FPID::SetLibNickname( const wxString& aLogical )
  170. {
  171. return SetLibNickname( std::string( TO_UTF8( aLogical ) ) );
  172. }
  173. int FPID::SetFootprintName( const std::string& aFootprintName )
  174. {
  175. int separation = int( aFootprintName.find_first_of( "/" ) );
  176. if( separation != -1 )
  177. {
  178. nickname = aFootprintName.substr( separation+1 );
  179. return separation + (int) nickname.size() + 1;
  180. }
  181. else
  182. {
  183. footprint = aFootprintName;
  184. }
  185. return -1;
  186. }
  187. int FPID::SetFootprintName( const wxString& aFootprintName )
  188. {
  189. return SetFootprintName( std::string( TO_UTF8( aFootprintName ) ) );
  190. }
  191. int FPID::SetRevision( const std::string& aRevision )
  192. {
  193. int offset = okRevision( aRevision );
  194. if( offset == -1 )
  195. {
  196. revision = aRevision;
  197. }
  198. return offset;
  199. }
  200. UTF8 FPID::Format() const
  201. {
  202. UTF8 ret;
  203. if( nickname.size() )
  204. {
  205. ret += nickname;
  206. ret += ':';
  207. }
  208. ret += footprint;
  209. if( revision.size() )
  210. {
  211. ret += '/';
  212. ret += revision;
  213. }
  214. return ret;
  215. }
  216. UTF8 FPID::GetFootprintNameAndRev() const
  217. {
  218. UTF8 ret;
  219. if( revision.size() )
  220. {
  221. ret += '/';
  222. ret += revision;
  223. }
  224. return ret;
  225. }
  226. UTF8 FPID::Format( const std::string& aLogicalLib, const std::string& aFootprintName,
  227. const std::string& aRevision )
  228. throw( PARSE_ERROR )
  229. {
  230. UTF8 ret;
  231. int offset;
  232. if( aLogicalLib.size() )
  233. {
  234. offset = okLogical( aLogicalLib );
  235. if( offset != -1 )
  236. {
  237. THROW_PARSE_ERROR( _( "Illegal character found in logical library name" ),
  238. wxString::FromUTF8( aLogicalLib.c_str() ),
  239. aLogicalLib.c_str(),
  240. 0,
  241. offset );
  242. }
  243. ret += aLogicalLib;
  244. ret += ':';
  245. }
  246. if( aRevision.size() )
  247. {
  248. offset = okRevision( aRevision );
  249. if( offset != -1 )
  250. {
  251. THROW_PARSE_ERROR( _( "Illegal character found in revision" ),
  252. wxString::FromUTF8( aRevision.c_str() ),
  253. aRevision.c_str(),
  254. 0,
  255. offset );
  256. }
  257. ret += '/';
  258. ret += aRevision;
  259. }
  260. return ret;
  261. }
  262. int FPID::compare( const FPID& aFPID ) const
  263. {
  264. // Don't bother comparing the same object.
  265. if( this == &aFPID )
  266. return 0;
  267. int retv = nickname.compare( aFPID.nickname );
  268. if( retv != 0 )
  269. return retv;
  270. retv = footprint.compare( aFPID.footprint );
  271. if( retv != 0 )
  272. return retv;
  273. return revision.compare( aFPID.revision );
  274. }
  275. #if 0 && defined(DEBUG)
  276. // build this with Debug CMAKE_BUILD_TYPE
  277. void FPID::Test()
  278. {
  279. static const char* lpids[] = {
  280. "smt:R_0805/rev0",
  281. "mysmt:R_0805/rev2",
  282. "device:AXIAL-0500",
  283. };
  284. for( unsigned i=0; i<sizeof(lpids)/sizeof(lpids[0]); ++i )
  285. {
  286. // test some round tripping
  287. FPID lpid( lpids[i] ); // parse
  288. // format
  289. printf( "input:'%s' full:'%s' nickname: %s footprint:'%s' rev:'%s'\n",
  290. lpids[i],
  291. lpid.Format().c_str(),
  292. lpid.GetLibNickname().c_str(),
  293. lpid.GetFootprintName().c_str(),
  294. lpid.GetRevision().c_str() );
  295. }
  296. }
  297. int main( int argc, char** argv )
  298. {
  299. FPID::Test();
  300. return 0;
  301. }
  302. #endif