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.

551 lines
13 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2010-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright (C) 2012-2017 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 2012-2017 KiCad Developers, see AUTHORS.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 <wx/filename.h>
  26. #include <wx/uri.h>
  27. #include <set>
  28. #include <fctsys.h>
  29. #include <common.h>
  30. #include <macros.h>
  31. #include <kiface_i.h>
  32. #include <lib_table_lexer.h>
  33. #include <lib_table_base.h>
  34. #define OPT_SEP '|' ///< options separator character
  35. using namespace LIB_TABLE_T;
  36. LIB_TABLE_ROW* new_clone( const LIB_TABLE_ROW& aRow )
  37. {
  38. return aRow.clone();
  39. }
  40. void LIB_TABLE_ROW::setProperties( PROPERTIES* aProperties )
  41. {
  42. properties.reset( aProperties );
  43. }
  44. void LIB_TABLE_ROW::SetFullURI( const wxString& aFullURI )
  45. {
  46. uri_user = aFullURI;
  47. #if !FP_LATE_ENVVAR
  48. uri_expanded = FP_LIB_TABLE::ExpandSubstitutions( aFullURI );
  49. #endif
  50. }
  51. const wxString LIB_TABLE_ROW::GetFullURI( bool aSubstituted ) const
  52. {
  53. if( aSubstituted )
  54. {
  55. #if !FP_LATE_ENVVAR // early expansion
  56. return uri_expanded;
  57. #else // late expansion
  58. return LIB_TABLE::ExpandSubstitutions( uri_user );
  59. #endif
  60. }
  61. return uri_user;
  62. }
  63. void LIB_TABLE_ROW::Format( OUTPUTFORMATTER* out, int nestLevel ) const
  64. {
  65. // In Kicad, we save path and file names using the Unix notation (separator = '/')
  66. // So ensure separator is always '/' is saved URI string
  67. wxString uri = GetFullURI();
  68. uri.Replace( '\\', '/' );
  69. wxString extraOptions;
  70. if( !GetIsEnabled() )
  71. {
  72. extraOptions += "(disabled)";
  73. }
  74. out->Print( nestLevel, "(lib (name %s)(type %s)(uri %s)(options %s)(descr %s)%s)\n",
  75. out->Quotew( GetNickName() ).c_str(),
  76. out->Quotew( GetType() ).c_str(),
  77. out->Quotew( uri ).c_str(),
  78. out->Quotew( GetOptions() ).c_str(),
  79. out->Quotew( GetDescr() ).c_str(),
  80. extraOptions.ToStdString().c_str()
  81. );
  82. }
  83. void LIB_TABLE_ROW::Parse( std::unique_ptr< LIB_TABLE_ROW >& aRow, LIB_TABLE_LEXER* in )
  84. {
  85. /*
  86. * (lib (name NICKNAME)(descr DESCRIPTION)(type TYPE)(full_uri FULL_URI)(options OPTIONS))
  87. *
  88. * Elements after (name) are order independent.
  89. */
  90. T tok = in->NextTok();
  91. if( tok != T_lib )
  92. in->Expecting( T_lib );
  93. // (name NICKNAME)
  94. in->NeedLEFT();
  95. if( ( tok = in->NextTok() ) != T_name )
  96. in->Expecting( T_name );
  97. in->NeedSYMBOLorNUMBER();
  98. aRow->SetNickName( in->FromUTF8() );
  99. in->NeedRIGHT();
  100. // After (name), remaining (lib) elements are order independent, and in
  101. // some cases optional.
  102. bool sawType = false;
  103. bool sawOpts = false;
  104. bool sawDesc = false;
  105. bool sawUri = false;
  106. while( ( tok = in->NextTok() ) != T_RIGHT )
  107. {
  108. if( tok == T_EOF )
  109. in->Unexpected( T_EOF );
  110. if( tok != T_LEFT )
  111. in->Expecting( T_LEFT );
  112. tok = in->NeedSYMBOLorNUMBER();
  113. switch( tok )
  114. {
  115. case T_uri:
  116. if( sawUri )
  117. in->Duplicate( tok );
  118. sawUri = true;
  119. in->NeedSYMBOLorNUMBER();
  120. // Saved path and file names use the Unix notation (separator = '/')
  121. // However old files, and files edited by hands can use the woindows
  122. // separator. Force the unix notation
  123. // (It works on windows, and moreover, wxFileName and wxDir takes care to that
  124. // on windows)
  125. // moreover, URLs use the '/' as separator
  126. {
  127. wxString uri = in->FromUTF8();
  128. uri.Replace( '\\', '/' );
  129. aRow->SetFullURI( uri );
  130. }
  131. break;
  132. case T_type:
  133. if( sawType )
  134. in->Duplicate( tok );
  135. sawType = true;
  136. in->NeedSYMBOLorNUMBER();
  137. aRow->SetType( in->FromUTF8() );
  138. break;
  139. case T_options:
  140. if( sawOpts )
  141. in->Duplicate( tok );
  142. sawOpts = true;
  143. in->NeedSYMBOLorNUMBER();
  144. aRow->SetOptions( in->FromUTF8() );
  145. break;
  146. case T_descr:
  147. if( sawDesc )
  148. in->Duplicate( tok );
  149. sawDesc = true;
  150. in->NeedSYMBOLorNUMBER();
  151. aRow->SetDescr( in->FromUTF8() );
  152. break;
  153. default:
  154. in->Unexpected( tok );
  155. }
  156. in->NeedRIGHT();
  157. }
  158. if( !sawType )
  159. in->Expecting( T_type );
  160. if( !sawUri )
  161. in->Expecting( T_uri );
  162. }
  163. bool LIB_TABLE_ROW::operator==( const LIB_TABLE_ROW& r ) const
  164. {
  165. return nickName == r.nickName
  166. && uri_user == r.uri_user
  167. && options == r.options
  168. && description == r.description
  169. && enabled == r.enabled;
  170. }
  171. void LIB_TABLE_ROW::SetOptions( const wxString& aOptions )
  172. {
  173. options = aOptions;
  174. // set PROPERTIES* from options
  175. setProperties( LIB_TABLE::ParseOptions( TO_UTF8( aOptions ) ) );
  176. }
  177. LIB_TABLE::LIB_TABLE( LIB_TABLE* aFallBackTable ) :
  178. fallBack( aFallBackTable )
  179. {
  180. // not copying fall back, simply search aFallBackTable separately
  181. // if "nickName not found".
  182. }
  183. LIB_TABLE::~LIB_TABLE()
  184. {
  185. // *fallBack is not owned here.
  186. }
  187. bool LIB_TABLE::IsEmpty( bool aIncludeFallback )
  188. {
  189. if( !aIncludeFallback || !fallBack )
  190. return rows.empty();
  191. return rows.empty() && fallBack->IsEmpty( true );
  192. }
  193. const wxString LIB_TABLE::GetDescription( const wxString& aNickname )
  194. {
  195. // use "no exception" form of find row:
  196. const LIB_TABLE_ROW* row = findRow( aNickname );
  197. if( row )
  198. return row->GetDescr();
  199. else
  200. return wxEmptyString;
  201. }
  202. bool LIB_TABLE::HasLibrary( const wxString& aNickname, bool aCheckEnabled ) const
  203. {
  204. const LIB_TABLE_ROW* row = findRow( aNickname );
  205. if( row == nullptr )
  206. return false;
  207. if( aCheckEnabled && !row->GetIsEnabled() )
  208. return false;
  209. return true;
  210. }
  211. wxString LIB_TABLE::GetFullURI( const wxString& aNickname, bool aExpandEnvVars ) const
  212. {
  213. const LIB_TABLE_ROW* row = findRow( aNickname );
  214. wxString retv;
  215. if( row )
  216. retv = row->GetFullURI( aExpandEnvVars );
  217. return retv;
  218. }
  219. LIB_TABLE_ROW* LIB_TABLE::findRow( const wxString& aNickName ) const
  220. {
  221. LIB_TABLE* cur = (LIB_TABLE*) this;
  222. do
  223. {
  224. cur->ensureIndex();
  225. INDEX_CITER it = cur->nickIndex.find( aNickName );
  226. if( it != cur->nickIndex.end() )
  227. {
  228. return &cur->rows[it->second]; // found
  229. }
  230. // not found, search fall back table(s), if any
  231. } while( ( cur = cur->fallBack ) != 0 );
  232. return nullptr; // not found
  233. }
  234. LIB_TABLE_ROW* LIB_TABLE::findRow( const wxString& aNickName )
  235. {
  236. LIB_TABLE* cur = (LIB_TABLE*) this;
  237. do
  238. {
  239. cur->ensureIndex();
  240. INDEX_ITER it = cur->nickIndex.find( aNickName );
  241. if( it != cur->nickIndex.end() )
  242. {
  243. return &cur->rows[it->second]; // found
  244. }
  245. // not found, search fall back table(s), if any
  246. } while( ( cur = cur->fallBack ) != 0 );
  247. return nullptr; // not found
  248. }
  249. const LIB_TABLE_ROW* LIB_TABLE::FindRowByURI( const wxString& aURI )
  250. {
  251. LIB_TABLE* cur = this;
  252. do
  253. {
  254. cur->ensureIndex();
  255. for( unsigned i = 0; i < cur->rows.size(); i++ )
  256. {
  257. wxString tmp = cur->rows[i].GetFullURI( true );
  258. if( tmp.Find( "://" ) != wxNOT_FOUND )
  259. {
  260. if( tmp == aURI )
  261. return &cur->rows[i]; // found as URI
  262. }
  263. else
  264. {
  265. wxFileName fn = aURI;
  266. // This will also test if the file is a symlink so if we are comparing
  267. // a symlink to the same real file, the comparison will be true. See
  268. // wxFileName::SameAs() in the wxWidgets source.
  269. if( fn == wxFileName( tmp ) )
  270. return &cur->rows[i]; // found as full path and file name
  271. }
  272. }
  273. // not found, search fall back table(s), if any
  274. } while( ( cur = cur->fallBack ) != 0 );
  275. return nullptr; // not found
  276. }
  277. std::vector<wxString> LIB_TABLE::GetLogicalLibs()
  278. {
  279. // Only return unique logical library names. Use std::set::insert() to
  280. // quietly reject any duplicates, which can happen when encountering a duplicate
  281. // nickname from one of the fall back table(s).
  282. std::set< wxString > unique;
  283. std::vector< wxString > ret;
  284. const LIB_TABLE* cur = this;
  285. do
  286. {
  287. for( LIB_TABLE_ROWS_CITER it = cur->rows.begin(); it!=cur->rows.end(); ++it )
  288. {
  289. if( it->GetIsEnabled() )
  290. {
  291. unique.insert( it->GetNickName() );
  292. }
  293. }
  294. } while( ( cur = cur->fallBack ) != 0 );
  295. ret.reserve( unique.size() );
  296. // return a sorted, unique set of nicknames in a std::vector<wxString> to caller
  297. for( std::set< wxString >::const_iterator it = unique.begin(); it!=unique.end(); ++it )
  298. {
  299. ret.push_back( *it );
  300. }
  301. return ret;
  302. }
  303. bool LIB_TABLE::InsertRow( LIB_TABLE_ROW* aRow, bool doReplace )
  304. {
  305. ensureIndex();
  306. INDEX_CITER it = nickIndex.find( aRow->GetNickName() );
  307. if( it == nickIndex.end() )
  308. {
  309. rows.push_back( aRow );
  310. nickIndex.insert( INDEX_VALUE( aRow->GetNickName(), rows.size() - 1 ) );
  311. return true;
  312. }
  313. if( doReplace )
  314. {
  315. rows.replace( it->second, aRow );
  316. return true;
  317. }
  318. return false;
  319. }
  320. void LIB_TABLE::Load( const wxString& aFileName )
  321. {
  322. // It's OK if footprint library tables are missing.
  323. if( wxFileName::IsFileReadable( aFileName ) )
  324. {
  325. FILE_LINE_READER reader( aFileName );
  326. LIB_TABLE_LEXER lexer( &reader );
  327. Parse( &lexer );
  328. }
  329. }
  330. void LIB_TABLE::Save( const wxString& aFileName ) const
  331. {
  332. FILE_OUTPUTFORMATTER sf( aFileName );
  333. Format( &sf, 0 );
  334. }
  335. PROPERTIES* LIB_TABLE::ParseOptions( const std::string& aOptionsList )
  336. {
  337. if( aOptionsList.size() )
  338. {
  339. const char* cp = &aOptionsList[0];
  340. const char* end = cp + aOptionsList.size();
  341. PROPERTIES props;
  342. std::string pair;
  343. // Parse all name=value pairs
  344. while( cp < end )
  345. {
  346. pair.clear();
  347. // Skip leading white space.
  348. while( cp < end && isspace( *cp ) )
  349. ++cp;
  350. // Find the end of pair/field
  351. while( cp < end )
  352. {
  353. if( *cp == '\\' && cp + 1 < end && cp[1] == OPT_SEP )
  354. {
  355. ++cp; // skip the escape
  356. pair += *cp++; // add the separator
  357. }
  358. else if( *cp == OPT_SEP )
  359. {
  360. ++cp; // skip the separator
  361. break; // process the pair
  362. }
  363. else
  364. pair += *cp++;
  365. }
  366. // stash the pair
  367. if( pair.size() )
  368. {
  369. // first equals sign separates 'name' and 'value'.
  370. size_t eqNdx = pair.find( '=' );
  371. if( eqNdx != pair.npos )
  372. {
  373. std::string name = pair.substr( 0, eqNdx );
  374. std::string value = pair.substr( eqNdx + 1 );
  375. props[name] = value;
  376. }
  377. else
  378. props[pair] = ""; // property is present, but with no value.
  379. }
  380. }
  381. if( props.size() )
  382. return new PROPERTIES( props );
  383. }
  384. return nullptr;
  385. }
  386. UTF8 LIB_TABLE::FormatOptions( const PROPERTIES* aProperties )
  387. {
  388. UTF8 ret;
  389. if( aProperties )
  390. {
  391. for( PROPERTIES::const_iterator it = aProperties->begin(); it != aProperties->end(); ++it )
  392. {
  393. const std::string& name = it->first;
  394. const UTF8& value = it->second;
  395. if( ret.size() )
  396. ret += OPT_SEP;
  397. ret += name;
  398. // the separation between name and value is '='
  399. if( value.size() )
  400. {
  401. ret += '=';
  402. for( std::string::const_iterator si = value.begin(); si != value.end(); ++si )
  403. {
  404. // escape any separator in the value.
  405. if( *si == OPT_SEP )
  406. ret += '\\';
  407. ret += *si;
  408. }
  409. }
  410. }
  411. }
  412. return ret;
  413. }
  414. const wxString LIB_TABLE::ExpandSubstitutions( const wxString& aString )
  415. {
  416. return ExpandEnvVarSubstitutions( aString );
  417. }