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