KiCad PCB EDA Suite
lib_table_base.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 <[email protected]>
5 * Copyright (C) 2012 Wayne Stambaugh <[email protected]>
6 * Copyright (C) 2012-2022 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 <wx/filename.h>
28#include <set>
29#include <common.h>
30#include <kiface_base.h>
31#include <lib_table_base.h>
32#include <lib_table_lexer.h>
33#include <macros.h>
34#include <string_utils.h>
35
36#define OPT_SEP '|'
37
38
39using namespace LIB_TABLE_T;
40
41
43{
44 return aRow.clone();
45}
46
47
49{
50 properties.reset( aProperties );
51}
52
53
54void LIB_TABLE_ROW::SetFullURI( const wxString& aFullURI )
55{
56 uri_user = aFullURI;
57
58#if !FP_LATE_ENVVAR
59 uri_expanded = FP_LIB_TABLE::ExpandSubstitutions( aFullURI );
60#endif
61}
62
63
64const wxString LIB_TABLE_ROW::GetFullURI( bool aSubstituted ) const
65{
66 if( aSubstituted )
67 {
68#if !FP_LATE_ENVVAR // early expansion
69 return uri_expanded;
70
71#else // late expansion
72 return ExpandEnvVarSubstitutions( uri_user, nullptr );
73#endif
74 }
75
76 return uri_user;
77}
78
79
80void LIB_TABLE_ROW::Format( OUTPUTFORMATTER* out, int nestLevel ) const
81{
82 // In Kicad, we save path and file names using the Unix notation (separator = '/')
83 // So ensure separator is always '/' is saved URI string
84 wxString uri = GetFullURI();
85 uri.Replace( '\\', '/' );
86
87 wxString extraOptions;
88
89 if( !GetIsEnabled() )
90 extraOptions += "(disabled)";
91
92 if( !GetIsVisible() )
93 extraOptions += "(hidden)";
94
95 out->Print( nestLevel, "(lib (name %s)(type %s)(uri %s)(options %s)(descr %s)%s)\n",
96 out->Quotew( GetNickName() ).c_str(),
97 out->Quotew( GetType() ).c_str(),
98 out->Quotew( uri ).c_str(),
99 out->Quotew( GetOptions() ).c_str(),
100 out->Quotew( GetDescr() ).c_str(),
101 extraOptions.ToStdString().c_str() );
102}
103
104
106{
107 return nickName == r.nickName
108 && uri_user == r.uri_user
109 && options == r.options
110 && description == r.description
111 && enabled == r.enabled
112 && visible == r.visible;
113}
114
115
116void LIB_TABLE_ROW::SetOptions( const wxString& aOptions )
117{
118 options = aOptions;
119
120 // set PROPERTIES* from options
122}
123
124
125LIB_TABLE::LIB_TABLE( LIB_TABLE* aFallBackTable ) :
126 fallBack( aFallBackTable )
127{
128 // not copying fall back, simply search aFallBackTable separately
129 // if "nickName not found".
130}
131
132
134{
135 // *fallBack is not owned here.
136}
137
138
139bool LIB_TABLE::IsEmpty( bool aIncludeFallback )
140{
141 if( !aIncludeFallback || !fallBack )
142 return rows.empty();
143
144 return rows.empty() && fallBack->IsEmpty( true );
145}
146
147
148const wxString LIB_TABLE::GetDescription( const wxString& aNickname )
149{
150 // Use "no exception" form of find row and ignore disabled flag.
151 const LIB_TABLE_ROW* row = findRow( aNickname );
152
153 if( row )
154 return row->GetDescr();
155 else
156 return wxEmptyString;
157}
158
159
160bool LIB_TABLE::HasLibrary( const wxString& aNickname, bool aCheckEnabled ) const
161{
162 const LIB_TABLE_ROW* row = findRow( aNickname, aCheckEnabled );
163
164 if( row == nullptr )
165 return false;
166
167 return true;
168}
169
170
171bool LIB_TABLE::HasLibraryWithPath( const wxString& aPath ) const
172{
173 for( const LIB_TABLE_ROW& row : rows )
174 {
175 if( row.GetFullURI() == aPath )
176 return true;
177 }
178
179 return false;
180}
181
182
183wxString LIB_TABLE::GetFullURI( const wxString& aNickname, bool aExpandEnvVars ) const
184{
185 const LIB_TABLE_ROW* row = findRow( aNickname, true );
186
187 wxString retv;
188
189 if( row )
190 retv = row->GetFullURI( aExpandEnvVars );
191
192 return retv;
193}
194
195
196LIB_TABLE_ROW* LIB_TABLE::findRow( const wxString& aNickName, bool aCheckIfEnabled ) const
197{
198 LIB_TABLE_ROW* row = nullptr;
199 LIB_TABLE* cur = (LIB_TABLE*) this;
200
201 do
202 {
203 cur->ensureIndex();
204
205 for( const std::pair<const wxString, int>& entry : cur->nickIndex )
206 {
207 if( entry.first == aNickName )
208 {
209 row = &cur->rows[entry.second];
210
211 if( !aCheckIfEnabled || row->GetIsEnabled() )
212 return row;
213 }
214 }
215
216 // Repeat, this time looking for names that were "fixed" by legacy versions because
217 // the old eeschema file format didn't support spaces in tokens.
218 for( const std::pair<const wxString, int>& entry : cur->nickIndex )
219 {
220 wxString legacyLibName = entry.first;
221 legacyLibName.Replace( " ", "_" );
222
223 if( legacyLibName == aNickName )
224 {
225 row = &cur->rows[entry.second];
226
227 if( !aCheckIfEnabled || row->GetIsEnabled() )
228 return row;
229 }
230 }
231
232 // not found, search fall back table(s), if any
233 } while( ( cur = cur->fallBack ) != nullptr );
234
235 return nullptr; // not found
236}
237
238
239const LIB_TABLE_ROW* LIB_TABLE::FindRowByURI( const wxString& aURI )
240{
241 LIB_TABLE* cur = this;
242
243 do
244 {
245 cur->ensureIndex();
246
247 for( unsigned i = 0; i < cur->rows.size(); i++ )
248 {
249 wxString tmp = cur->rows[i].GetFullURI( true );
250
251 if( tmp.Find( "://" ) != wxNOT_FOUND )
252 {
253 if( tmp == aURI )
254 return &cur->rows[i]; // found as URI
255 }
256 else
257 {
258 wxFileName fn = aURI;
259
260 // This will also test if the file is a symlink so if we are comparing
261 // a symlink to the same real file, the comparison will be true. See
262 // wxFileName::SameAs() in the wxWidgets source.
263 if( fn == wxFileName( tmp ) )
264 return &cur->rows[i]; // found as full path and file name
265 }
266 }
267
268 // not found, search fall back table(s), if any
269 } while( ( cur = cur->fallBack ) != nullptr );
270
271 return nullptr; // not found
272}
273
274
275std::vector<wxString> LIB_TABLE::GetLogicalLibs()
276{
277 // Only return unique logical library names. Use std::set::insert() to quietly reject any
278 // duplicates (usually due to encountering a duplicate nickname in a fallback table).
279
280 std::set<wxString> unique;
281 std::vector<wxString> ret;
282 const LIB_TABLE* cur = this;
283
284 do
285 {
286 for( LIB_TABLE_ROWS_CITER it = cur->rows.begin(); it!=cur->rows.end(); ++it )
287 {
288 if( it->GetIsEnabled() )
289 unique.insert( it->GetNickName() );
290 }
291
292 } while( ( cur = cur->fallBack ) != nullptr );
293
294 ret.reserve( unique.size() );
295
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 ret.push_back( *it );
299
300 // We want to allow case-sensitive duplicates but sort by case-insensitive ordering
301 std::sort( ret.begin(), ret.end(),
302 []( const wxString& lhs, const wxString& rhs )
303 {
304 return StrNumCmp( lhs, rhs, true /* ignore case */ ) < 0;
305 } );
306
307 return ret;
308}
309
310
311bool LIB_TABLE::InsertRow( LIB_TABLE_ROW* aRow, bool doReplace )
312{
313 ensureIndex();
314
315 std::lock_guard<std::mutex> lock( m_nickIndexMutex );
316
317 INDEX_CITER it = nickIndex.find( aRow->GetNickName() );
318
319 aRow->SetParent( this );
320
321 if( it == nickIndex.end() )
322 {
323 rows.push_back( aRow );
324 nickIndex.insert( INDEX_VALUE( aRow->GetNickName(), rows.size() - 1 ) );
325 return true;
326 }
327
328 if( doReplace )
329 {
330 rows.replace( it->second, aRow );
331 return true;
332 }
333
334 return false;
335}
336
337
338void LIB_TABLE::Load( const wxString& aFileName )
339{
340 // It's OK if footprint library tables are missing.
341 if( wxFileName::IsFileReadable( aFileName ) )
342 {
343 FILE_LINE_READER reader( aFileName );
344 LIB_TABLE_LEXER lexer( &reader );
345
346 Parse( &lexer );
347 }
348}
349
350
351void LIB_TABLE::Save( const wxString& aFileName ) const
352{
353 FILE_OUTPUTFORMATTER sf( aFileName );
354 Format( &sf, 0 );
355}
356
357
358STRING_UTF8_MAP* LIB_TABLE::ParseOptions( const std::string& aOptionsList )
359{
360 if( aOptionsList.size() )
361 {
362 const char* cp = &aOptionsList[0];
363 const char* end = cp + aOptionsList.size();
364
365 STRING_UTF8_MAP props;
366 std::string pair;
367
368 // Parse all name=value pairs
369 while( cp < end )
370 {
371 pair.clear();
372
373 // Skip leading white space.
374 while( cp < end && isspace( *cp ) )
375 ++cp;
376
377 // Find the end of pair/field
378 while( cp < end )
379 {
380 if( *cp == '\\' && cp + 1 < end && cp[1] == OPT_SEP )
381 {
382 ++cp; // skip the escape
383 pair += *cp++; // add the separator
384 }
385 else if( *cp == OPT_SEP )
386 {
387 ++cp; // skip the separator
388 break; // process the pair
389 }
390 else
391 {
392 pair += *cp++;
393 }
394 }
395
396 // stash the pair
397 if( pair.size() )
398 {
399 // first equals sign separates 'name' and 'value'.
400 size_t eqNdx = pair.find( '=' );
401
402 if( eqNdx != pair.npos )
403 {
404 std::string name = pair.substr( 0, eqNdx );
405 std::string value = pair.substr( eqNdx + 1 );
406 props[name] = value;
407 }
408 else
409 {
410 props[pair] = ""; // property is present, but with no value.
411 }
412 }
413 }
414
415 if( props.size() )
416 return new STRING_UTF8_MAP( props );
417 }
418
419 return nullptr;
420}
421
422
424{
425 UTF8 ret;
426
427 if( aProperties )
428 {
429 for( STRING_UTF8_MAP::const_iterator it = aProperties->begin(); it != aProperties->end(); ++it )
430 {
431 const std::string& name = it->first;
432
433 const UTF8& value = it->second;
434
435 if( ret.size() )
436 ret += OPT_SEP;
437
438 ret += name;
439
440 // the separation between name and value is '='
441 if( value.size() )
442 {
443 ret += '=';
444
445 for( std::string::const_iterator si = value.begin(); si != value.end(); ++si )
446 {
447 // escape any separator in the value.
448 if( *si == OPT_SEP )
449 ret += '\\';
450
451 ret += *si;
452 }
453 }
454 }
455 }
456
457 return ret;
458}
const char * name
Definition: DXF_plotter.cpp:56
A LINE_READER that reads from an open file.
Definition: richio.h:173
Used for text file output.
Definition: richio.h:457
Hold a record identifying a library accessed by the appropriate plug in object in the LIB_TABLE.
void SetFullURI(const wxString &aFullURI)
Change the full URI for the library.
bool visible
Whether the LIB_TABLE_ROW is visible in choosers.
const wxString & GetOptions() const
Return the options string, which may hold a password or anything else needed to instantiate the under...
wxString nickName
const wxString & GetDescr() const
Return the description of the library referenced by this row.
std::unique_ptr< STRING_UTF8_MAP > properties
wxString options
wxString uri_user
what user entered from UI or loaded from disk
virtual const wxString GetType() const =0
Return the type of library represented by this row.
wxString description
void Format(OUTPUTFORMATTER *out, int nestLevel) const
Serialize this object as utf8 text to an OUTPUTFORMATTER, and tries to make it look good using multip...
bool enabled
Whether the LIB_TABLE_ROW is enabled.
void SetParent(LIB_TABLE *aParent)
void setProperties(STRING_UTF8_MAP *aProperties)
const wxString & GetNickName() const
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...
LIB_TABLE_ROW * clone() const
bool operator==(const LIB_TABLE_ROW &r) const
bool GetIsEnabled() const
void SetOptions(const wxString &aOptions)
Change the library options strings.
bool GetIsVisible() const
Manage LIB_TABLE_ROW records (rows), and can be searched based on library nickname.
const wxString GetDescription(const wxString &aNickname)
std::vector< wxString > GetLogicalLibs()
Return the logical library names, all of them that are pertinent to a look up done on this LIB_TABLE.
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
bool InsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Adds aRow if it does not already exist or if doReplace is true.
virtual ~LIB_TABLE()
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
bool HasLibraryWithPath(const wxString &aPath) const
Test for the existence of aPath in the library table.
LIB_TABLE_ROWS rows
virtual void Format(OUTPUTFORMATTER *aOutput, int aIndentLevel) const =0
Generate the table in s-expression format to aOutput with an indentation level of aIndentLevel.
INDEX::value_type INDEX_VALUE
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
LIB_TABLE(LIB_TABLE *aFallBackTable=nullptr)
Build a library table by pre-pending this table fragment in front of aFallBackTable.
bool IsEmpty(bool aIncludeFallback=true)
Return true if the table is empty.
virtual void Parse(LIB_TABLE_LEXER *aLexer)=0
Parse the #LIB_TABLE_LEXER s-expression library table format into the appropriate LIB_TABLE_ROW objec...
INDEX::const_iterator INDEX_CITER
LIB_TABLE * fallBack
const LIB_TABLE_ROW * FindRowByURI(const wxString &aURI)
std::mutex m_nickIndexMutex
Mutex to protect access to the nickIndex variable.
void Save(const wxString &aFileName) const
Write this library table to aFileName in s-expression form.
void ensureIndex()
INDEX nickIndex
this particular key is the nickName within each row.
static STRING_UTF8_MAP * ParseOptions(const std::string &aOptionsList)
Parses aOptionsList and places the result into a #PROPERTIES object which is returned.
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,...
static UTF8 FormatOptions(const STRING_UTF8_MAP *aProperties)
Returns a list of options from the aProperties parameter.
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:310
std::string Quotew(const wxString &aWrapee) const
Definition: richio.cpp:499
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:431
A name/value tuple with unique names and optional values.
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:71
std::string::const_iterator begin() const
Definition: utf8.h:192
std::string::size_type size() const
Definition: utf8.h:110
std::string::const_iterator end() const
Definition: utf8.h:193
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:267
The common library.
E_SERIE r
Definition: eserie.cpp:41
LIB_TABLE_ROW * new_clone(const LIB_TABLE_ROW &aRow)
Allows boost pointer containers to make clones of the data stored in them.
#define OPT_SEP
options separator character
LIB_TABLE_ROWS::const_iterator LIB_TABLE_ROWS_CITER
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96