KiCad PCB EDA Suite
lib_id.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 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2012-2016 Wayne Stambaugh <stambaughw@verizon.net>
6  * Copyright (C) 2010-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 
26 #include <cstring>
27 #include <memory>
28 #include <wx/wx.h> // _()
29 
30 #include <macros.h> // TO_UTF8()
31 #include <lib_id.h>
32 #include <kicad_string.h>
33 
34 
35 static inline bool isDigit( char c )
36 {
37  return c >= '0' && c <= '9';
38 }
39 
40 
41 const char* EndsWithRev( const char* start, const char* tail, char separator )
42 {
43  bool sawDigit = false;
44 
45  while( tail > start && isDigit( *--tail ) )
46  {
47  sawDigit = true;
48  }
49 
50  // if sawDigit, tail points to the 'v' here.
51 
52  if( sawDigit && tail-3 >= start )
53  {
54  tail -= 3;
55 
56  if( tail[0]==separator && tail[1]=='r' && tail[2]=='e' && tail[3]=='v' )
57  {
58  return tail+1; // omit separator, return "revN[N..]"
59  }
60  }
61 
62  return 0;
63 }
64 
65 
66 #if 0 // Not used
67 int RevCmp( const char* s1, const char* s2 )
68 {
69  int r = strncmp( s1, s2, 3 );
70 
71  if( r || strlen(s1)<4 || strlen(s2)<4 )
72  {
73  return r;
74  }
75 
76  int rnum1 = atoi( s1+3 );
77  int rnum2 = atoi( s2+3 );
78 
79  return -(rnum1 - rnum2); // swap the sign, higher revs first
80 }
81 #endif
82 
83 //----<Policy and field test functions>-------------------------------------
84 
85 
86 static inline int okLogical( const std::string& aField )
87 {
88  // std::string::npos is largest positive number, casting to int makes it -1.
89  // Returning that means success.
90  return int( aField.find_first_of( ":" ) );
91 }
92 
93 
94 static int okRevision( const std::string& aField )
95 {
96  char rev[32]; // C string for speed
97 
98  if( aField.size() >= 4 )
99  {
100  strcpy( rev, "x/" );
101  strncat( rev, aField.c_str(), sizeof(rev)-strlen(rev)-1 );
102 
103  if( EndsWithRev( rev, rev + strlen(rev), '/' ) == rev+2 )
104  return -1; // success
105  }
106 
107  return 0; // first character position "is in error", is best we can do.
108 }
109 
110 
111 //----</Policy and field test functions>-------------------------------------
112 
113 
115 {
116  nickname.clear();
117  item_name.clear();
118  revision.clear();
119 }
120 
121 
122 int LIB_ID::Parse( const UTF8& aId )
123 {
124  clear();
125 
126  const char* buffer = aId.c_str();
127  const char* rev = EndsWithRev( buffer, buffer+aId.length(), '/' );
128  size_t revNdx;
129  size_t partNdx;
130  int offset;
131 
132  //=====<revision>=========================================
133  // in a LIB_ID like discret:R3/rev4
134  if( rev )
135  {
136  revNdx = rev - buffer;
137 
138  // no need to check revision, EndsWithRev did that.
139  revision = aId.substr( revNdx );
140  --revNdx; // back up to omit the '/' which precedes the rev
141  }
142  else
143  {
144  revNdx = aId.size();
145  }
146 
147  //=====<nickname>==========================================
148  if( ( partNdx = aId.find( ':' ) ) != aId.npos )
149  {
150  offset = SetLibNickname( aId.substr( 0, partNdx ) );
151 
152  if( offset > -1 )
153  {
154  return offset;
155  }
156 
157  ++partNdx; // skip ':'
158  }
159  else
160  {
161  partNdx = 0;
162  }
163 
164  //=====<item name>====================================
165  if( partNdx >= revNdx )
166  return partNdx; // Error: no library item name.
167 
168  // Be sure the item name is valid.
169  // Some chars can be found in legacy files converted files from an other EDA tools.
170  std::string fpname = aId.substr( partNdx, revNdx-partNdx );
171  ReplaceIllegalFileNameChars( &fpname, '_' );
172  SetLibItemName( fpname );
173 
174  return -1;
175 }
176 
177 
178 LIB_ID::LIB_ID( const std::string& aId )
179 {
180  int offset = Parse( aId );
181 
182  if( offset != -1 )
183  {
184  THROW_PARSE_ERROR( _( "Illegal character found in LIB_ID string" ),
185  wxString::FromUTF8( aId.c_str() ),
186  aId.c_str(),
187  0,
188  offset );
189  }
190 }
191 
192 
193 LIB_ID::LIB_ID( const wxString& aId )
194 {
195  UTF8 id = TO_UTF8( aId );
196 
197  int offset = Parse( id );
198 
199  if( offset != -1 )
200  {
201  THROW_PARSE_ERROR( _( "Illegal character found in LIB_ID string" ),
202  aId,
203  id.c_str(),
204  0,
205  offset );
206  }
207 }
208 
209 
210 LIB_ID::LIB_ID( const wxString& aLibName, const wxString& aLibItemName,
211  const wxString& aRevision ) :
212  nickname( TO_UTF8( aLibName ) ),
213  item_name( TO_UTF8( aLibItemName ) ),
214  revision( TO_UTF8( aRevision ) )
215 {
216 }
217 
218 
219 int LIB_ID::SetLibNickname( const UTF8& aLogical )
220 {
221  int offset = okLogical( aLogical );
222 
223  if( offset == -1 )
224  {
225  nickname = aLogical;
226  }
227 
228  return offset;
229 }
230 
231 
232 int LIB_ID::SetLibItemName( const UTF8& aLibItemName, bool aTestForRev )
233 {
234  int separation = int( aLibItemName.find_first_of( "/" ) );
235 
236  if( aTestForRev && separation != -1 )
237  {
238  item_name = aLibItemName.substr( 0, separation-1 );
239  return separation;
240  }
241  else
242  {
243  item_name = aLibItemName;
244  }
245 
246  return -1;
247 }
248 
249 
250 int LIB_ID::SetRevision( const UTF8& aRevision )
251 {
252  int offset = okRevision( aRevision );
253 
254  if( offset == -1 )
255  {
256  revision = aRevision;
257  }
258 
259  return offset;
260 }
261 
262 
264 {
265  UTF8 ret;
266 
267  if( nickname.size() )
268  {
269  ret += nickname;
270  ret += ':';
271  }
272 
273  ret += item_name;
274 
275  if( revision.size() )
276  {
277  ret += '/';
278  ret += revision;
279  }
280 
281  return ret;
282 }
283 
284 
286 {
287  UTF8 ret;
288 
289  if( revision.size() )
290  {
291  ret += '/';
292  ret += revision;
293  }
294 
295  return ret;
296 }
297 
298 
299 UTF8 LIB_ID::Format( const UTF8& aLogicalLib, const UTF8& aLibItemName, const UTF8& aRevision )
300 {
301  UTF8 ret;
302  int offset;
303 
304  if( aLogicalLib.size() )
305  {
306  offset = okLogical( aLogicalLib );
307 
308  if( offset != -1 )
309  {
310  THROW_PARSE_ERROR( _( "Illegal character found in logical library name" ),
311  wxString::FromUTF8( aLogicalLib.c_str() ),
312  aLogicalLib.c_str(), 0, offset );
313  }
314 
315  ret += aLogicalLib;
316  ret += ':';
317  }
318 
319  ret += aLibItemName; // TODO: Add validity test.
320 
321  if( aRevision.size() )
322  {
323  offset = okRevision( aRevision );
324 
325  if( offset != -1 )
326  {
327  THROW_PARSE_ERROR( _( "Illegal character found in revision" ),
328  wxString::FromUTF8( aRevision.c_str() ),
329  aRevision.c_str(),
330  0,
331  offset );
332  }
333 
334  ret += '/';
335  ret += aRevision;
336  }
337 
338  return ret;
339 }
340 
341 
342 int LIB_ID::compare( const LIB_ID& aLibId ) const
343 {
344  // Don't bother comparing the same object.
345  if( this == &aLibId )
346  return 0;
347 
348  int retv = nickname.compare( aLibId.nickname );
349 
350  if( retv != 0 )
351  return retv;
352 
353  retv = item_name.compare( aLibId.item_name );
354 
355  if( retv != 0 )
356  return retv;
357 
358  return revision.compare( aLibId.revision );
359 }
360 
361 
362 #if 0 && defined(DEBUG)
363 
364 // build this with Debug CMAKE_BUILD_TYPE
365 
366 void LIB_ID::Test()
367 {
368  static const char* lpids[] = {
369  "smt:R_0805/rev0",
370  "mysmt:R_0805/rev2",
371  "device:AXIAL-0500",
372  };
373 
374  for( unsigned i=0; i<sizeof(lpids)/sizeof(lpids[0]); ++i )
375  {
376  // test some round tripping
377 
378  LIB_ID lpid( lpids[i] ); // parse
379 
380  // format
381  printf( "input:'%s' full:'%s' nickname: %s item_name:'%s' rev:'%s'\n",
382  lpids[i],
383  lpid.Format().c_str(),
384  lpid.GetLibNickname().c_str(),
385  lpid.GetLibItemName().c_str(),
386  lpid.GetRevision().c_str() );
387  }
388 }
389 
390 
391 int main( int argc, char** argv )
392 {
393  LIB_ID::Test();
394 
395  return 0;
396 }
397 
398 #endif
int compare(const LIB_ID &aLibId) const
Compare the contents of LIB_ID objects by performing a std::string comparison of the library nickname...
Definition: lib_id.cpp:342
Class UTF8 is an 8 bit std::string that is assuredly encoded in UTF8, and supplies special conversion...
Definition: utf8.h:53
const char * EndsWithRev(const char *start, const char *tail, char separator)
Definition: lib_id.cpp:41
UTF8 revision
The revision of the entry.
Definition: lib_id.h:222
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Function ReplaceIllegalFileNameChars checks aName for illegal file name characters.
Definition: string.cpp:483
int Parse(const UTF8 &aId)
Function Parse.
Definition: lib_id.cpp:122
static int okLogical(const std::string &aField)
Definition: lib_id.cpp:86
Class LIB_ID.
Definition: lib_id.h:56
UTF8 GetLibItemNameAndRev() const
Definition: lib_id.cpp:285
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
UTF8 substr(size_t pos=0, size_t len=npos) const
Definition: utf8.h:104
static int okRevision(const std::string &aField)
Definition: lib_id.cpp:94
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:133
int main(int argc, char **argv)
static bool isDigit(char c)
Definition: lib_id.cpp:35
UTF8 item_name
The name of the entry in the logical library.
Definition: lib_id.h:221
int SetRevision(const UTF8 &aRevision)
Definition: lib_id.cpp:250
UTF8 nickname
The nickname of the library or empty.
Definition: lib_id.h:220
void clear()
Function clear.
Definition: lib_id.cpp:114
int SetLibItemName(const UTF8 &aLibItemName, bool aTestForRev=true)
Function SetLibItemName.
Definition: lib_id.cpp:232
int SetLibNickname(const UTF8 &aNickname)
Function SetLibNickname.
Definition: lib_id.cpp:219
LIB_ID()
Definition: lib_id.h:60
UTF8 Format() const
Function Format.
Definition: lib_id.cpp:263