KiCad PCB EDA Suite
fp_lib_table.cpp
Go to the documentation of this file.
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 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 2012-2021 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 
26 
27 #include <kiface_base.h>
28 #include <footprint_info.h>
29 #include <lib_id.h>
30 #include <lib_table_lexer.h>
31 #include <pgm_base.h>
32 #include <search_stack.h>
34 #include <systemdirsappend.h>
35 #include <fp_lib_table.h>
36 #include <footprint.h>
37 
38 #include <wx/hash.h>
39 
40 #define OPT_SEP '|'
41 
42 using namespace LIB_TABLE_T;
43 
44 
45 static const wxChar global_tbl_name[] = wxT( "fp-lib-table" );
46 
47 
49 {
50  return LIB_TABLE_ROW::operator == ( aRow ) && type == aRow.type;
51 }
52 
53 
54 void FP_LIB_TABLE_ROW::SetType( const wxString& aType )
55 {
56  type = IO_MGR::EnumFromStr( aType );
57 
58  if( IO_MGR::PCB_FILE_T( -1 ) == type )
59  type = IO_MGR::KICAD_SEXP;
60 
61  plugin.release();
62 }
63 
64 
66  LIB_TABLE( aFallBackTable )
67 {
68  // not copying fall back, simply search aFallBackTable separately
69  // if "nickName not found".
70 }
71 
72 
73 void FP_LIB_TABLE::Parse( LIB_TABLE_LEXER* in )
74 {
75  T tok;
76  wxString errMsg; // to collect error messages
77 
78  // This table may be nested within a larger s-expression, or not.
79  // Allow for parser of that optional containing s-epression to have looked ahead.
80  if( in->CurTok() != T_fp_lib_table )
81  {
82  in->NeedLEFT();
83 
84  if( ( tok = in->NextTok() ) != T_fp_lib_table )
85  in->Expecting( T_fp_lib_table );
86  }
87 
88  while( ( tok = in->NextTok() ) != T_RIGHT )
89  {
90  std::unique_ptr<FP_LIB_TABLE_ROW> row = std::make_unique<FP_LIB_TABLE_ROW>();
91 
92  if( tok == T_EOF )
93  in->Expecting( T_RIGHT );
94 
95  if( tok != T_LEFT )
96  in->Expecting( T_LEFT );
97 
98  // in case there is a "row integrity" error, tell where later.
99  int lineNum = in->CurLineNumber();
100 
101  if( ( tok = in->NextTok() ) != T_lib )
102  in->Expecting( T_lib );
103 
104  // (name NICKNAME)
105  in->NeedLEFT();
106 
107  if( ( tok = in->NextTok() ) != T_name )
108  in->Expecting( T_name );
109 
110  in->NeedSYMBOLorNUMBER();
111 
112  row->SetNickName( in->FromUTF8() );
113 
114  in->NeedRIGHT();
115 
116  // After (name), remaining (lib) elements are order independent, and in
117  // some cases optional.
118  bool sawType = false;
119  bool sawOpts = false;
120  bool sawDesc = false;
121  bool sawUri = false;
122  bool sawDisabled = false;
123 
124  while( ( tok = in->NextTok() ) != T_RIGHT )
125  {
126  if( tok == T_EOF )
127  in->Unexpected( T_EOF );
128 
129  if( tok != T_LEFT )
130  in->Expecting( T_LEFT );
131 
132  tok = in->NeedSYMBOLorNUMBER();
133 
134  switch( tok )
135  {
136  case T_uri:
137  if( sawUri )
138  in->Duplicate( tok );
139  sawUri = true;
140  in->NeedSYMBOLorNUMBER();
141  row->SetFullURI( in->FromUTF8() );
142  break;
143 
144  case T_type:
145  if( sawType )
146  in->Duplicate( tok );
147  sawType = true;
148  in->NeedSYMBOLorNUMBER();
149  row->SetType( in->FromUTF8() );
150  break;
151 
152  case T_options:
153  if( sawOpts )
154  in->Duplicate( tok );
155  sawOpts = true;
156  in->NeedSYMBOLorNUMBER();
157  row->SetOptions( in->FromUTF8() );
158  break;
159 
160  case T_descr:
161  if( sawDesc )
162  in->Duplicate( tok );
163  sawDesc = true;
164  in->NeedSYMBOLorNUMBER();
165  row->SetDescr( in->FromUTF8() );
166  break;
167 
168  case T_disabled:
169  if( sawDisabled )
170  in->Duplicate( tok );
171  sawDisabled = true;
172  row->SetEnabled( false );
173  break;
174 
175  default:
176  in->Unexpected( tok );
177  }
178 
179  in->NeedRIGHT();
180  }
181 
182  if( !sawType )
183  in->Expecting( T_type );
184 
185  if( !sawUri )
186  in->Expecting( T_uri );
187 
188  // all nickNames within this table fragment must be unique, so we do not
189  // use doReplace in InsertRow(). (However a fallBack table can have a
190  // conflicting nickName and ours will supercede that one since in
191  // FindLib() we search this table before any fall back.)
192  wxString nickname = row->GetNickName(); // store it to be able to used it
193  // after row deletion if an error occurs
194  LIB_TABLE_ROW* tmp = row.release();
195 
196  if( !InsertRow( tmp ) )
197  {
198  delete tmp; // The table did not take ownership of the row.
199 
200  wxString msg = wxString::Format( _( "Duplicate library nickname '%s' found in "
201  "footprint library table file line %d." ),
202  nickname,
203  lineNum );
204 
205  if( !errMsg.IsEmpty() )
206  errMsg << '\n';
207 
208  errMsg << msg;
209  }
210  }
211 
212  if( !errMsg.IsEmpty() )
213  THROW_IO_ERROR( errMsg );
214 }
215 
216 
217 bool FP_LIB_TABLE::operator==( const FP_LIB_TABLE& aFpTable ) const
218 {
219  if( rows.size() == aFpTable.rows.size() )
220  {
221  for( unsigned i = 0; i < rows.size(); ++i )
222  {
223  if( (FP_LIB_TABLE_ROW&)rows[i] != (FP_LIB_TABLE_ROW&)aFpTable.rows[i] )
224  return false;
225  }
226 
227  return true;
228  }
229 
230  return false;
231 }
232 
233 
234 void FP_LIB_TABLE::Format( OUTPUTFORMATTER* aOutput, int aIndentLevel ) const
235 {
236  aOutput->Print( aIndentLevel, "(fp_lib_table\n" );
237 
238  for( LIB_TABLE_ROWS_CITER it = rows.begin(); it != rows.end(); ++it )
239  it->Format( aOutput, aIndentLevel+1 );
240 
241  aOutput->Print( aIndentLevel, ")\n" );
242 }
243 
244 
245 long long FP_LIB_TABLE::GenerateTimestamp( const wxString* aNickname )
246 {
247  long long hash = 0;
248 
249  if( aNickname )
250  {
251  const FP_LIB_TABLE_ROW* row = FindRow( *aNickname, true );
252 
253  wxCHECK( row && row->plugin, hash );
254 
255  return row->plugin->GetLibraryTimestamp( row->GetFullURI( true ) ) +
256  wxHashTable::MakeKey( *aNickname );
257  }
258 
259  for( const wxString& nickname : GetLogicalLibs() )
260  {
261  const FP_LIB_TABLE_ROW* row = FindRow( nickname, true );
262 
263  wxCHECK2( row && row->plugin, continue );
264 
265  hash += row->plugin->GetLibraryTimestamp( row->GetFullURI( true ) ) +
266  wxHashTable::MakeKey( nickname );
267  }
268 
269  return hash;
270 }
271 
272 
273 void FP_LIB_TABLE::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aNickname,
274  bool aBestEfforts )
275 {
276  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
277  wxASSERT( (PLUGIN*) row->plugin );
278  row->plugin->FootprintEnumerate( aFootprintNames, row->GetFullURI( true ), aBestEfforts,
279  row->GetProperties() );
280 }
281 
282 
283 void FP_LIB_TABLE::PrefetchLib( const wxString& aNickname )
284 {
285  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
286  wxASSERT( (PLUGIN*) row->plugin );
287  row->plugin->PrefetchLib( row->GetFullURI( true ), row->GetProperties() );
288 }
289 
290 
291 const FP_LIB_TABLE_ROW* FP_LIB_TABLE::FindRow( const wxString& aNickname, bool aCheckIfEnabled )
292 {
293  // Do not optimize this code. Is done this way specifically to fix a runtime
294  // error with clang 4.0.1.
295  LIB_TABLE_ROW* ltrow = findRow( aNickname, aCheckIfEnabled );
296  FP_LIB_TABLE_ROW* row = dynamic_cast< FP_LIB_TABLE_ROW* >( ltrow );
297 
298  if( !row )
299  {
300  wxString msg = wxString::Format( _( "fp-lib-table files contain no library named '%s'." ),
301  aNickname );
302 
303  THROW_IO_ERROR( msg );
304  }
305 
306  // We've been 'lazy' up until now, but it cannot be deferred any longer,
307  // instantiate a PLUGIN of the proper kind if it is not already in this
308  // FP_LIB_TABLE_ROW.
309  if( !row->plugin )
310  row->setPlugin( IO_MGR::PluginFind( row->type ) );
311 
312  return row;
313 }
314 
315 
316 static void setLibNickname( FOOTPRINT* aModule, const wxString& aNickname,
317  const wxString& aFootprintName )
318 {
319  // The library cannot know its own name, because it might have been renamed or moved.
320  // Therefore footprints cannot know their own library nickname when residing in
321  // a footprint library.
322  // Only at this API layer can we tell the footprint about its actual library nickname.
323  if( aModule )
324  {
325  // remove "const"-ness, I really do want to set nickname without
326  // having to copy the LIB_ID and its two strings, twice each.
327  LIB_ID& fpid = (LIB_ID&) aModule->GetFPID();
328 
329  // Catch any misbehaving plugin, which should be setting internal footprint name properly:
330  wxASSERT( aFootprintName == fpid.GetLibItemName().wx_str() );
331 
332  // and clearing nickname
333  wxASSERT( !fpid.GetLibNickname().size() );
334 
335  fpid.SetLibNickname( aNickname );
336  }
337 }
338 
339 
340 const FOOTPRINT* FP_LIB_TABLE::GetEnumeratedFootprint( const wxString& aNickname,
341  const wxString& aFootprintName )
342 {
343  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
344  wxASSERT( (PLUGIN*) row->plugin );
345 
346  return row->plugin->GetEnumeratedFootprint( row->GetFullURI( true ), aFootprintName,
347  row->GetProperties() );
348 }
349 
350 
351 bool FP_LIB_TABLE::FootprintExists( const wxString& aNickname, const wxString& aFootprintName )
352 {
353  try
354  {
355  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
356  wxASSERT( (PLUGIN*) row->plugin );
357 
358  return row->plugin->FootprintExists( row->GetFullURI( true ), aFootprintName,
359  row->GetProperties() );
360  }
361  catch( ... )
362  {
363  return false;
364  }
365 }
366 
367 
368 FOOTPRINT* FP_LIB_TABLE::FootprintLoad( const wxString& aNickname,
369  const wxString& aFootprintName, bool aKeepUUID )
370 {
371  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
372  wxASSERT( (PLUGIN*) row->plugin );
373 
374  FOOTPRINT* ret = row->plugin->FootprintLoad( row->GetFullURI( true ), aFootprintName,
375  aKeepUUID, row->GetProperties() );
376 
377  setLibNickname( ret, row->GetNickName(), aFootprintName );
378 
379  return ret;
380 }
381 
382 
384  const FOOTPRINT* aFootprint, bool aOverwrite )
385 {
386  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
387  wxASSERT( (PLUGIN*) row->plugin );
388 
389  if( !aOverwrite )
390  {
391  // Try loading the footprint to see if it already exists, caller wants overwrite
392  // protection, which is atypical, not the default.
393 
394  wxString fpname = aFootprint->GetFPID().GetLibItemName();
395 
396  std::unique_ptr<FOOTPRINT> footprint( row->plugin->FootprintLoad( row->GetFullURI( true ),
397  fpname,
398  row->GetProperties() ) );
399 
400  if( footprint.get() )
401  return SAVE_SKIPPED;
402  }
403 
404  row->plugin->FootprintSave( row->GetFullURI( true ), aFootprint, row->GetProperties() );
405 
406  return SAVE_OK;
407 }
408 
409 
410 void FP_LIB_TABLE::FootprintDelete( const wxString& aNickname, const wxString& aFootprintName )
411 {
412  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
413  wxASSERT( (PLUGIN*) row->plugin );
414  return row->plugin->FootprintDelete( row->GetFullURI( true ), aFootprintName,
415  row->GetProperties() );
416 }
417 
418 
419 bool FP_LIB_TABLE::IsFootprintLibWritable( const wxString& aNickname )
420 {
421  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
422  wxASSERT( (PLUGIN*) row->plugin );
423  return row->plugin->IsFootprintLibWritable( row->GetFullURI( true ) );
424 }
425 
426 
427 void FP_LIB_TABLE::FootprintLibDelete( const wxString& aNickname )
428 {
429  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
430  wxASSERT( (PLUGIN*) row->plugin );
431  row->plugin->FootprintLibDelete( row->GetFullURI( true ), row->GetProperties() );
432 }
433 
434 
435 void FP_LIB_TABLE::FootprintLibCreate( const wxString& aNickname )
436 {
437  const FP_LIB_TABLE_ROW* row = FindRow( aNickname, true );
438  wxASSERT( (PLUGIN*) row->plugin );
439  row->plugin->FootprintLibCreate( row->GetFullURI( true ), row->GetProperties() );
440 }
441 
442 
444  bool aKeepUUID )
445 {
446  wxString nickname = aFootprintId.GetLibNickname();
447  wxString fpname = aFootprintId.GetLibItemName();
448 
449  if( nickname.size() )
450  {
451  return FootprintLoad( nickname, fpname, aKeepUUID );
452  }
453 
454  // nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
455  else
456  {
457  std::vector<wxString> nicks = GetLogicalLibs();
458 
459  // Search each library going through libraries alphabetically.
460  for( unsigned i = 0; i < nicks.size(); ++i )
461  {
462  // FootprintLoad() returns NULL on not found, does not throw exception
463  // unless there's an IO_ERROR.
464  FOOTPRINT* ret = FootprintLoad( nicks[i], fpname, aKeepUUID );
465 
466  if( ret )
467  return ret;
468  }
469 
470  return nullptr;
471  }
472 }
473 
474 
476 {
477  return "KICAD6_FOOTPRINT_DIR";
478 }
479 
480 
482 {
483  bool tableExists = true;
484  wxFileName fn = GetGlobalTableFileName();
485 
486  if( !fn.FileExists() )
487  {
488  tableExists = false;
489 
490  if( !fn.DirExists() && !fn.Mkdir( 0x777, wxPATH_MKDIR_FULL ) )
491  {
492  THROW_IO_ERROR( wxString::Format( _( "Cannot create global library table path '%s'." ),
493  fn.GetPath() ) );
494  }
495 
496  // Attempt to copy the default global file table from the KiCad
497  // template folder to the user's home configuration path.
498  SEARCH_STACK ss;
499 
500  SystemDirsAppend( &ss );
501 
502  wxString templatePath =
503  Pgm().GetLocalEnvVariables().at( wxT( "KICAD6_TEMPLATE_DIR" ) ).GetValue();
504 
505  if( !templatePath.IsEmpty() )
506  ss.AddPaths( templatePath, 0 );
507 
508  wxString fileName = ss.FindValidPath( global_tbl_name );
509 
510  // The fallback is to create an empty global footprint table for the user to populate.
511  if( fileName.IsEmpty() || !::wxCopyFile( fileName, fn.GetFullPath(), false ) )
512  {
513  FP_LIB_TABLE emptyTable;
514 
515  emptyTable.Save( fn.GetFullPath() );
516  }
517  }
518 
519  aTable.Load( fn.GetFullPath() );
520 
521  return tableExists;
522 }
523 
524 
526 {
527  wxFileName fn;
528 
530  fn.SetName( global_tbl_name );
531 
532  return fn.GetFullPath();
533 }
void setPlugin(PLUGIN *aPlugin)
Definition: fp_lib_table.h:84
static const wxChar global_tbl_name[]
bool IsFootprintLibWritable(const wxString &aNickname)
Return true if the library given by aNickname is writable.
wxString FindValidPath(const wxString &aFileName) const
Definition: search_stack.h:70
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
Hold a record identifying a library accessed by the appropriate plug in object in the LIB_TABLE.
Hold a record identifying a library accessed by the appropriate footprint library PLUGIN object in th...
Definition: fp_lib_table.h:40
bool InsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Adds aRow if it does not already exist or if doReplace is true.
FOOTPRINT * FootprintLoadWithOptionalNickname(const LIB_ID &aFootprintId, bool aKeepUUID=false)
Load a footprint having aFootprintId with possibly an empty nickname.
FOOTPRINT * FootprintLoad(const wxString &aNickname, const wxString &aFootprintName, bool aKeepUUID=false)
Load a footprint having aFootprintName from the library given by aNickname.
System directories search utilities.
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:309
Look for files in a number of paths.
Definition: search_stack.h:41
LIB_TABLE_ROWS rows
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
void FootprintLibDelete(const wxString &aNickname)
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
bool operator==(const LIB_TABLE_ROW &r) const
static void setLibNickname(FOOTPRINT *aModule, const wxString &aNickname, const wxString &aFootprintName)
PLUGIN::RELEASER plugin
Definition: fp_lib_table.h:91
long long GenerateTimestamp(const wxString *aNickname)
Generate a hashed timestamp representing the last-mod-times of the library indicated by aNickname,...
const wxString GetFullURI(bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aNickname, bool aBestEfforts)
Return a list of footprint names contained within the library given by aNickname.
const wxString & GetNickName() const
static const wxString GlobalPathEnvVariableName()
Return the name of the environment variable used to hold the directory of locally installed "KiCad sp...
virtual void Parse(LIB_TABLE_LEXER *aLexer) override
Parse the #LIB_TABLE_LEXER s-expression library table format into the appropriate LIB_TABLE_ROW objec...
static bool LoadGlobalTable(FP_LIB_TABLE &aTable)
Load the global footprint library table into aTable.
static PCB_FILE_T EnumFromStr(const wxString &aFileType)
Return the PCB_FILE_T from the corresponding plugin type name: "kicad", "legacy", etc.
Definition: io_mgr.cpp:93
#define _(s)
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:90
void PrefetchLib(const wxString &aNickname)
If possible, prefetches the specified library (e.g.
void SystemDirsAppend(SEARCH_STACK *aSearchStack)
Append system places to aSearchStack in a platform specific way and pertinent to KiCad programs.
const LIB_ID & GetFPID() const
Definition: footprint.h:194
LIB_TABLE_ROW * findRow(const wxString &aNickname, bool aCheckIfEnabled=false) const
Return a LIB_TABLE_ROW if aNickname is found in this table or in any chained fallBack table fragment,...
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
const FOOTPRINT * GetEnumeratedFootprint(const wxString &aNickname, const wxString &aFootprintName)
A version of FootprintLoad() for use after FootprintEnumerate() for more efficient cache management.
virtual void Format(OUTPUTFORMATTER *aOutput, int aIndentLevel) const override
Generate the table in s-expression format to aOutput with an indentation level of aIndentLevel.
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
int SetLibNickname(const UTF8 &aNickname)
Override the logical library name portion of the LIB_ID to aNickname.
Definition: lib_id.cpp:97
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
bool operator==(const FP_LIB_TABLE &aFpTable) const
void FootprintLibCreate(const wxString &aNickname)
FP_LIB_TABLE(FP_LIB_TABLE *aFallBackTable=nullptr)
Build a footprint library table by pre-pending this table fragment in front of aFallBackTable.
SAVE_T
The set of return values from FootprintSave() below.
Definition: fp_lib_table.h:198
see class PGM_BASE
LIB_TABLE_ROWS::const_iterator LIB_TABLE_ROWS_CITER
const FP_LIB_TABLE_ROW * FindRow(const wxString &aNickName, bool aCheckIfEnabled=false)
Return an FP_LIB_TABLE_ROW if aNickName is found in this table or in any chained fall back table frag...
static PLUGIN * PluginFind(PCB_FILE_T aFileType)
Return a PLUGIN which the caller can use to import, export, save, or load design documents.
Definition: io_mgr.cpp:58
A base class that BOARD loading and saving plugins should derive from.
Definition: io_mgr.h:269
SAVE_T FootprintSave(const wxString &aNickname, const FOOTPRINT *aFootprint, bool aOverwrite=true)
Write aFootprint to an existing library given by aNickname.
wxString wx_str() const
Definition: utf8.cpp:46
void Save(const wxString &aFileName) const
Write this library table to aFileName in s-expression form.
const PROPERTIES * GetProperties() const
Return the constant PROPERTIES for this library (LIB_TABLE_ROW).
void SetType(const wxString &aType) override
Change the type represented by this row.
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:426
std::string::size_type size() const
Definition: utf8.h:110
void FootprintDelete(const wxString &aNickname, const wxString &aFootprintName)
Delete the aFootprintName from the library given by aNickname.
PCB_FILE_T
The set of file types that the IO_MGR knows about, and for which there has been a plugin written.
Definition: io_mgr.h:53
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
bool operator==(const FP_LIB_TABLE_ROW &aRow) const
void AddPaths(const wxString &aPaths, int aIndex=-1)
Insert or append path(s).
bool FootprintExists(const wxString &aNickname, const wxString &aFootprintName)
Indicates whether or not the given footprint already exists in the given library.
static wxString GetGlobalTableFileName()
std::vector< wxString > GetLogicalLibs()
Return the logical library names, all of them that are pertinent to a look up done on this LIB_TABLE.
S-expression Pcbnew file format.
Definition: io_mgr.h:56
Manage LIB_TABLE_ROW records (rows), and can be searched based on library nickname.