1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014-2017  Cirilo Bernardo
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 
26 #include <iostream>
27 #include <iomanip>
28 #include <sstream>
29 #include <cmath>
30 #include <cerrno>
31 #include <algorithm>
32 #include <utility>
33 #include <wx/string.h>
34 #include <wx/filename.h>
35 
36 #include "idf_parser.h"
37 #include "idf_helpers.h"
38 #include "streamwrapper.h"
39 
40 using namespace std;
41 using namespace IDF3;
42 
43 
MatchCompOutline(IDF3_COMP_OUTLINE * aOutlineA,IDF3_COMP_OUTLINE * aOutlineB)44 static bool MatchCompOutline( IDF3_COMP_OUTLINE* aOutlineA, IDF3_COMP_OUTLINE* aOutlineB )
45 {
46     if( aOutlineA->GetComponentClass() != aOutlineB->GetComponentClass() )
47         return false;
48 
49     if( aOutlineA->OutlinesSize() != aOutlineB->OutlinesSize() )
50         return false;
51 
52     // are both outlines empty?
53     if( aOutlineA->OutlinesSize() == 0 )
54         return true;
55 
56     IDF_OUTLINE* opA = aOutlineA->GetOutline( 0 );
57     IDF_OUTLINE* opB = aOutlineB->GetOutline( 0 );
58 
59     if( opA->size() != opB->size() )
60         return false;
61 
62     if( opA->size() == 0 )
63         return true;
64 
65     std::list<IDF_SEGMENT*>::iterator olAs = opA->begin();
66     std::list<IDF_SEGMENT*>::iterator olAe = opA->end();
67     std::list<IDF_SEGMENT*>::iterator olBs = opB->begin();
68 
69     while( olAs != olAe )
70     {
71         if( !(*olAs)->MatchesStart( (*olBs)->startPoint ) )
72             return false;
73 
74         if( !(*olAs)->MatchesEnd( (*olBs)->endPoint ) )
75             return false;
76 
77         ++olAs;
78         ++olBs;
79     }
80 
81     return true;
82 }
83 
84 
IDF3_COMP_OUTLINE_DATA()85 IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA()
86 {
87     parent = nullptr;
88     outline = nullptr;
89     xoff = 0.0;
90     yoff = 0.0;
91     zoff = 0.0;
92     aoff = 0.0;
93 }
94 
95 
IDF3_COMP_OUTLINE_DATA(IDF3_COMPONENT * aParent,IDF3_COMP_OUTLINE * aOutline)96 IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
97                                                 IDF3_COMP_OUTLINE* aOutline )
98 {
99     parent = aParent;
100     outline = aOutline;
101     xoff = 0.0;
102     yoff = 0.0;
103     zoff = 0.0;
104     aoff = 0.0;
105 
106     if( aOutline )
107         aOutline->incrementRef();
108 }
109 
110 
IDF3_COMP_OUTLINE_DATA(IDF3_COMPONENT * aParent,IDF3_COMP_OUTLINE * aOutline,double aXoff,double aYoff,double aZoff,double aAngleOff)111 IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
112                                                 IDF3_COMP_OUTLINE* aOutline,
113                                                 double aXoff, double aYoff,
114                                                 double aZoff, double aAngleOff )
115 {
116     parent = aParent;
117     outline = aOutline;
118     xoff = aXoff;
119     yoff = aYoff;
120     zoff = aZoff;
121     aoff = aAngleOff;
122 }
123 
124 
~IDF3_COMP_OUTLINE_DATA()125 IDF3_COMP_OUTLINE_DATA::~IDF3_COMP_OUTLINE_DATA()
126 {
127     if( outline )
128         outline->decrementRef();
129 }
130 
131 
132 #ifndef DISABLE_IDF_OWNERSHIP
checkOwnership(int aSourceLine,const char * aSourceFunc)133 bool IDF3_COMP_OUTLINE_DATA::checkOwnership( int aSourceLine, const char* aSourceFunc )
134 {
135     if( !parent )
136     {
137         ostringstream ostr;
138         ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
139         ostr << "* BUG: IDF3_COMP_OUTLINE_DATA::parent not set; cannot enforce ownership rules\n";
140         errormsg = ostr.str();
141 
142         return false;
143     }
144 
145     IDF3::IDF_PLACEMENT placement = parent->GetPlacement();
146     IDF3::CAD_TYPE parentCAD = parent->GetCadType();
147 
148     if( placement == PS_PLACED || placement == PS_UNPLACED )
149         return true;
150 
151     if( placement == PS_MCAD && parentCAD == CAD_MECH )
152         return true;
153 
154     if( placement == PS_ECAD && parentCAD == CAD_ELEC )
155         return true;
156 
157     do
158     {
159         ostringstream ostr;
160         ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
161         ostr << "* ownership violation; CAD type is ";
162 
163         if( parentCAD == CAD_MECH )
164             ostr << "MCAD ";
165         else
166             ostr << "ECAD ";
167 
168         ostr << "while outline owner is " << GetPlacementString( placement ) << "\n";
169         errormsg = ostr.str();
170 
171     } while( 0 );
172 
173     return false;
174 }
175 #endif
176 
177 
SetOffsets(double aXoff,double aYoff,double aZoff,double aAngleOff)178 bool IDF3_COMP_OUTLINE_DATA::SetOffsets( double aXoff, double aYoff,
179                                          double aZoff, double aAngleOff )
180 {
181 #ifndef DISABLE_IDF_OWNERSHIP
182     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
183         return false;
184 #endif
185 
186     xoff = aXoff;
187     yoff = aYoff;
188     zoff = aZoff;
189     aoff = aAngleOff;
190     return true;
191 }
192 
193 
GetOffsets(double & aXoff,double & aYoff,double & aZoff,double & aAngleOff)194 void IDF3_COMP_OUTLINE_DATA::GetOffsets( double& aXoff, double& aYoff,
195                                          double& aZoff, double& aAngleOff )
196 {
197     aXoff = xoff;
198     aYoff = yoff;
199     aZoff = zoff;
200     aAngleOff = aoff;
201 }
202 
203 
SetParent(IDF3_COMPONENT * aParent)204 void IDF3_COMP_OUTLINE_DATA::SetParent( IDF3_COMPONENT* aParent )
205 {
206     parent = aParent;
207 }
208 
209 
SetOutline(IDF3_COMP_OUTLINE * aOutline)210 bool IDF3_COMP_OUTLINE_DATA::SetOutline( IDF3_COMP_OUTLINE* aOutline )
211 {
212 #ifndef DISABLE_IDF_OWNERSHIP
213     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
214         return false;
215 #endif
216 
217     if( outline )
218         outline->decrementRef();
219 
220     outline = aOutline;
221 
222     if( outline )
223         outline->incrementRef();
224 
225     return true;
226 }
227 
228 
readPlaceData(std::istream & aBoardFile,IDF3::FILE_STATE & aBoardState,IDF3_BOARD * aBoard,IDF3::IDF_VERSION aIdfVersion,bool aNoSubstituteOutlines)229 bool IDF3_COMP_OUTLINE_DATA::readPlaceData( std::istream &aBoardFile,
230                                             IDF3::FILE_STATE& aBoardState,
231                                             IDF3_BOARD *aBoard,
232                                             IDF3::IDF_VERSION aIdfVersion,
233                                             bool aNoSubstituteOutlines )
234 {
235     if( !aBoard )
236         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
237                           "\n* BUG: invoked with no reference to the parent IDF_BOARD" ) );
238 
239     // clear out data possibly left over from previous use of the object
240     outline = nullptr;
241     parent  = nullptr;
242 
243     std::string iline;      // the input line
244     bool isComment;         // true if a line just read in is a comment line
245     std::streampos pos;
246     int idx = 0;
247     bool quoted = false;
248     std::string token;
249     std::string uid;
250     std::string refdes;
251     IDF3::IDF_PLACEMENT placement = IDF3::PS_UNPLACED;
252     IDF3::IDF_LAYER side = IDF3::LYR_TOP;
253 
254     // RECORD 2: 'package name', 'part number', 'Refdes' (any, NOREFDES, BOARD)
255     while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
256 
257     if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
258     {
259         ostringstream ostr;
260 
261         ostr << "invalid IDF file\n";
262         ostr << "* violation: could not read PLACEMENT section\n";
263         ostr << "* file position: " << pos;
264 
265         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
266     }
267 
268     if( isComment )
269     {
270         ostringstream ostr;
271 
272         ostr << "invalid IDF file\n";
273         ostr << "* violation: comment within PLACEMENT section\n";
274         ostr << "* line: '" << iline << "'\n";
275         ostr << "* file position: " << pos;
276 
277         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
278     }
279 
280     idx = 0;
281     GetIDFString( iline, token, quoted, idx );
282 
283     if( !quoted && CompareToken( ".END_PLACEMENT", token ) )
284     {
285         aBoardState = IDF3::FILE_PLACEMENT;
286         return false;
287     }
288 
289     std::string ngeom = token;
290 
291     if( !GetIDFString( iline, token, quoted, idx ) )
292     {
293         ostringstream ostr;
294 
295         ostr << "invalid IDF file\n";
296         ostr << "* violation: no PART NAME in PLACEMENT RECORD2\n";
297         ostr << "* line: '" << iline << "'\n";
298         ostr << "* file position: " << pos;
299 
300         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
301     }
302 
303     std::string npart = token;
304     uid = ngeom + "_" + npart;
305 
306     if( !GetIDFString( iline, token, quoted, idx ) )
307     {
308         ostringstream ostr;
309 
310         ostr << "invalid IDF file\n";
311         ostr << "* violation: no REFDES in PLACEMENT RECORD2\n";
312         ostr << "* line: '" << iline << "'\n";
313         ostr << "* file position: " << pos;
314 
315         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
316     }
317 
318     if( CompareToken( "NOREFDES", token ) )
319     {
320         // according to the IDF3.0 specification, this is a
321         // mechanical component. The specification is defective
322         // since it is impossible to associate mechanical
323         // components with their holes unless the mechanical
324         // component is given a unique RefDes. This class of defect
325         // is one reason IDF does not work well in faithfully
326         // conveying information between ECAD and MCAD.
327         refdes = aBoard->GetNewRefDes();
328     }
329     else if( CompareToken( "BOARD", token ) )
330     {
331         ostringstream ostr;
332 
333         ostr << "UNSUPPORTED FEATURE\n";
334         ostr << "* RefDes is 'BOARD', indicating this is a PANEL FILE (not supported)\n";
335         ostr << "* line: '" << iline << "'\n";
336         ostr << "* file position: " << pos;
337 
338         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
339     }
340     else if( CompareToken( "PANEL", token ) )
341     {
342         ostringstream ostr;
343 
344         ostr << "invalid IDF file\n";
345         ostr << "* violation: RefDes in PLACEMENT RECORD2 is 'PANEL'\n";
346         ostr << "* line: '" << iline << "'\n";
347         ostr << "* file position: " << pos;
348 
349         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
350     }
351     else if( token.empty() )
352     {
353         ostringstream ostr;
354 
355         ostr << "invalid IDF file\n";
356         ostr << "* violation: empty RefDes string in PLACEMENT RECORD2\n";
357         ostr << "* line: '" << iline << "'\n";
358         ostr << "* file position: " << pos;
359 
360         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
361     }
362     else
363     {
364         // note: perversely, spaces can be a valid RefDes
365         refdes = token;
366     }
367 
368     // V2: RECORD 3: X, Y, ROT, SIDE (top/bot), PLACEMENT (fixed, placed, unplaced)
369     // V3: RECORD 3: X, Y, Z, ROT, SIDE (top/bot), PLACEMENT (placed, unplaced, mcad, ecad)
370     while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
371 
372     if( !aBoardFile.good() )
373     {
374         ostringstream ostr;
375 
376         ostr << "invalid IDF file\n";
377         ostr << "* problems reading PLACEMENT SECTION, RECORD 3\n";
378         ostr << "* file position: " << pos;
379 
380         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
381     }
382 
383     if( isComment )
384     {
385         ostringstream ostr;
386 
387         ostr << "invalid IDF file\n";
388         ostr << "* violation: comment within PLACEMENT section\n";
389         ostr << "* line: '" << iline << "'\n";
390         ostr << "* file position: " << pos;
391 
392         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
393     }
394 
395     idx = 0;
396     GetIDFString( iline, token, quoted, idx );
397 
398     if( quoted )
399     {
400         ostringstream ostr;
401 
402         ostr << "invalid IDF file\n";
403         ostr << "* violation: X value must not be in quotes (PLACEMENT RECORD 3)\n";
404         ostr << "* line: '" << iline << "'\n";
405         ostr << "* file position: " << pos;
406 
407         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
408     }
409 
410     istringstream istr;
411     istr.str( token );
412 
413     istr >> xoff;
414     if( istr.fail() )
415     {
416         ostringstream ostr;
417 
418         ostr << "invalid IDF file\n";
419         ostr << "* violation: X value is not numeric (PLACEMENT RECORD 3)\n";
420         ostr << "* line: '" << iline << "'\n";
421         ostr << "* file position: " << pos;
422 
423         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
424     }
425 
426     if( !GetIDFString( iline, token, quoted, idx ) )
427     {
428         ostringstream ostr;
429 
430         ostr << "invalid IDF file\n";
431         ostr << "* violation: no Y value (PLACEMENT RECORD 3)\n";
432         ostr << "* line: '" << iline << "'\n";
433         ostr << "* file position: " << pos;
434 
435         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
436     }
437 
438     istr.clear();
439     istr.str( token );
440 
441     istr >> yoff;
442     if( istr.fail() )
443     {
444         ostringstream ostr;
445 
446         ostr << "invalid IDF file\n";
447         ostr << "* violation: Y value is not numeric (PLACEMENT RECORD 3)\n";
448         ostr << "* line: '" << iline << "'\n";
449         ostr << "* file position: " << pos;
450 
451         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
452     }
453 
454     if( aIdfVersion > IDF_V2 )
455     {
456         if( !GetIDFString( iline, token, quoted, idx ) )
457         {
458             ostringstream ostr;
459 
460             ostr << "invalid IDFv3 file\n";
461             ostr << "* violation: no Z value (PLACEMENT RECORD 3)\n";
462             ostr << "* line: '" << iline << "'\n";
463             ostr << "* file position: " << pos;
464 
465             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
466         }
467 
468         istr.clear();
469         istr.str( token );
470 
471         istr >> zoff;
472         if( istr.fail() )
473         {
474             ostringstream ostr;
475 
476             ostr << "invalid IDFv3 file\n";
477             ostr << "* violation: Z value is not numeric (PLACEMENT RECORD 3)\n";
478             ostr << "* line: '" << iline << "'\n";
479             ostr << "* file position: " << pos;
480 
481             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
482         }
483     }
484 
485     if( !GetIDFString( iline, token, quoted, idx ) )
486     {
487         ostringstream ostr;
488 
489         ostr << "invalid IDF file\n";
490         ostr << "* violation: no rotation value (PLACEMENT RECORD 3)\n";
491         ostr << "* line: '" << iline << "'\n";
492         ostr << "* file position: " << pos;
493 
494         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
495     }
496 
497     istr.clear();
498     istr.str( token );
499 
500     istr >> aoff;
501     if( istr.fail() )
502     {
503         ostringstream ostr;
504 
505         ostr << "invalid IDF file\n";
506         ostr << "* violation: rotation value is not numeric (PLACEMENT RECORD 3)\n";
507         ostr << "* line: '" << iline << "'\n";
508         ostr << "* file position: " << pos;
509 
510         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
511     }
512 
513     if( !GetIDFString( iline, token, quoted, idx ) )
514     {
515         ostringstream ostr;
516 
517         ostr << "invalid IDF file\n";
518         ostr << "* violation: no SIDE value (PLACEMENT RECORD 3)\n";
519         ostr << "* line: '" << iline << "'\n";
520         ostr << "* file position: " << pos;
521 
522         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
523     }
524 
525     if( CompareToken( "TOP", token ) )
526     {
527         side = IDF3::LYR_TOP;
528     }
529     else if( CompareToken( "BOTTOM", token ) )
530     {
531         side = IDF3::LYR_BOTTOM;
532     }
533     else
534     {
535         ostringstream ostr;
536 
537         ostr << "invalid IDF file\n";
538         ostr << "* violation: invalid SIDE value in PLACEMENT RECORD 3 ('";
539         ostr << token << "'); must be one of TOP/BOTTOM\n";
540         ostr << "* line: '" << iline << "'\n";
541         ostr << "* file position: " << pos;
542 
543         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
544     }
545 
546     if( !GetIDFString( iline, token, quoted, idx ) )
547     {
548         ostringstream ostr;
549 
550         ostr << "invalid IDF file\n";
551         ostr << "* violation: no PLACEMENT value in PLACEMENT RECORD 3\n";
552         ostr << "* line: '" << iline << "'\n";
553         ostr << "* file position: " << pos;
554 
555         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
556     }
557 
558     if( CompareToken( "PLACED", token ) )
559     {
560         placement = IDF3::PS_PLACED;
561     }
562     else if( CompareToken( "UNPLACED", token ) )
563     {
564         placement = IDF3::PS_UNPLACED;
565     }
566     else if( aIdfVersion > IDF_V2 && CompareToken( "MCAD", token ) )
567     {
568         placement = IDF3::PS_MCAD;
569     }
570     else if( aIdfVersion > IDF_V2 && CompareToken( "ECAD", token ) )
571     {
572         placement = IDF3::PS_ECAD;
573     }
574     else if( aIdfVersion < IDF_V3 && CompareToken( "FIXED", token ) )
575     {
576         if( aBoard->GetCadType() == CAD_ELEC )
577             placement = IDF3::PS_MCAD;
578         else
579             placement = IDF3::PS_ECAD;
580     }
581     else
582     {
583         ostringstream ostr;
584 
585         ostr << "invalid IDF file\n";
586         ostr << "* violation: invalid PLACEMENT value ('";
587         ostr << token << "') in PLACEMENT RECORD 3\n";
588         ostr << "* line: '" << iline << "'\n";
589         ostr << "* file position: " << pos;
590 
591         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
592     }
593 
594     outline = aBoard->GetComponentOutline( uid );
595 
596     if( outline == nullptr && !aNoSubstituteOutlines )
597     {
598         ERROR_IDF << "MISSING OUTLINE\n";
599         cerr << "* GeomName( " << ngeom << " ), PartName( " << npart << " )\n";
600         cerr << "* Substituting default outline.\n";
601         outline = aBoard->GetInvalidOutline( ngeom, npart );
602 
603         if( outline == nullptr )
604             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
605                               "\n* missing outline: cannot create default" ) );
606     }
607 
608     if( aBoard->GetUnit() == IDF3::UNIT_THOU )
609     {
610         xoff *= IDF_THOU_TO_MM;
611         yoff *= IDF_THOU_TO_MM;
612         zoff *= IDF_THOU_TO_MM;
613     }
614 
615     parent = aBoard->FindComponent( refdes );
616 
617     if( parent == nullptr )
618     {
619         IDF3_COMPONENT* cp = new IDF3_COMPONENT( aBoard );
620 
621         if( cp == nullptr )
622         {
623             outline = nullptr;
624 
625             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
626                               "cannot create component object" ) );
627         }
628 
629         cp->SetRefDes( refdes );
630         cp->SetPosition( xoff, yoff, aoff, side );
631         cp->SetPlacement( placement );
632 
633         xoff = 0;
634         yoff = 0;
635         aoff = 0;
636 
637         aBoard->AddComponent( cp );
638 
639         parent = cp;
640     }
641     else
642     {
643         double tX, tY, tA;
644         IDF3::IDF_LAYER tL;
645 
646         if( parent->GetPosition( tX, tY, tA, tL ) )
647         {
648             if( side != tL )
649             {
650                 outline = nullptr;
651                 ostringstream ostr;
652 
653                 ostr << "invalid IDF file\n";
654                 ostr << "* violation: inconsistent PLACEMENT data; ";
655                 ostr << "* SIDE value has changed from " << GetLayerString( tL );
656                 ostr << " to " << GetLayerString( side ) << "\n";
657                 ostr << "* line: '" << iline << "'\n";
658                 ostr << "* file position: " << pos;
659 
660                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
661             }
662 
663             xoff -= tX;
664             yoff -= tY;
665             aoff -= tA;
666         }
667         else
668         {
669             parent->SetPosition( xoff, yoff, aoff, side );
670             parent->SetPlacement( placement );
671 
672             xoff = 0;
673             yoff = 0;
674             aoff = 0;
675         }
676 
677         if( placement != parent->GetPlacement() )
678         {
679             outline = nullptr;
680             ostringstream ostr;
681 
682             ostr << "invalid IDF file\n";
683             ostr << "* violation: inconsistent PLACEMENT data; ";
684             ostr << "* PLACEMENT value has changed from ";
685             ostr << GetPlacementString( parent->GetPlacement() );
686             ostr << " to " << GetPlacementString( placement ) << "\n";
687             ostr << "* line: '" << iline << "'\n";
688             ostr << "* file position: " << pos;
689 
690             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
691         }
692 
693     }
694 
695     // copy internal data to a new object and push it into the component's outline list
696     IDF3_COMP_OUTLINE_DATA* cdp = new IDF3_COMP_OUTLINE_DATA;
697     *cdp = *this;
698     if( outline ) outline->incrementRef();
699     outline = nullptr;
700 
701     if( !parent->AddOutlineData( cdp ) )
702     {
703         delete cdp;
704 
705         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
706                           "could not add outline data object" ) );
707     }
708 
709     return true;
710 }
711 
712 
writePlaceData(std::ostream & aBoardFile,double aXpos,double aYpos,double aAngle,const std::string & aRefDes,IDF3::IDF_PLACEMENT aPlacement,IDF3::IDF_LAYER aSide)713 void IDF3_COMP_OUTLINE_DATA::writePlaceData( std::ostream& aBoardFile,
714                                              double aXpos, double aYpos, double aAngle,
715                                              const std::string& aRefDes,
716                                              IDF3::IDF_PLACEMENT aPlacement,
717                                              IDF3::IDF_LAYER aSide )
718 {
719     if( outline == nullptr )
720         return;
721 
722     if( outline->GetUID().empty() )
723         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
724                           "empty GEOM and PART names" ) );
725 
726     if( aPlacement == PS_INVALID )
727     {
728         ERROR_IDF << "placement invalid (" << aRefDes << ":";
729         aPlacement = PS_PLACED;
730     }
731 
732     if( aSide != LYR_TOP && aSide != LYR_BOTTOM )
733     {
734         ostringstream ostr;
735         ostr << "\n* invalid side (" << GetLayerString( aSide ) << "); ";
736         ostr << "must be TOP or BOTTOM\n";
737 
738         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
739     }
740 
741     // calculate the final position based on layer
742     double xpos, ypos, ang;
743 
744     switch( aSide )
745     {
746         case LYR_TOP:
747             xpos = aXpos + xoff;
748             ypos = aYpos + yoff;
749             ang  = aAngle + aoff;
750             break;
751 
752         default:
753             xpos = aXpos - xoff;
754             ypos = aYpos + yoff;
755             ang  = aAngle - aoff;
756             break;
757     }
758 
759     std::string arefdes = aRefDes;
760 
761     if( arefdes.empty() || !arefdes.compare( "~" )
762         || ( arefdes.size() >= 8 && CompareToken( "NOREFDES", arefdes.substr(0, 8) ) ) )
763         arefdes = "NOREFDES";
764 
765     aBoardFile << "\"" << outline->GetGeomName() << "\" \"" << outline->GetPartName() << "\" "
766     << arefdes << "\n";
767 
768     IDF3::IDF_UNIT unit = UNIT_MM;
769 
770     if( parent )
771         unit = parent->GetUnit();
772 
773     if( unit == UNIT_MM )
774     {
775         aBoardFile << setiosflags(ios::fixed) << setprecision(5) << xpos << " "
776         << ypos << " " << setprecision(3) << zoff << " "
777         << ang << " ";
778     }
779     else
780     {
781         aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (xpos / IDF_THOU_TO_MM) << " "
782         << (ypos / IDF_THOU_TO_MM) << " "  << (zoff / IDF_THOU_TO_MM) << " "
783         << setprecision(3) << ang << " ";
784     }
785 
786     WriteLayersText( aBoardFile, aSide );
787 
788     switch( aPlacement )
789     {
790         case PS_PLACED:
791             aBoardFile << " PLACED\n";
792             break;
793 
794         case PS_UNPLACED:
795             aBoardFile << " UNPLACED\n";
796             break;
797 
798         case PS_MCAD:
799             aBoardFile << " MCAD\n";
800             break;
801 
802         default:
803             aBoardFile << " ECAD\n";
804             break;
805     }
806 }
807 
808 
IDF3_COMPONENT(IDF3_BOARD * aParent)809 IDF3_COMPONENT::IDF3_COMPONENT( IDF3_BOARD* aParent )
810 {
811     xpos   = 0.0;
812     ypos   = 0.0;
813     angle  = 0.0;
814 
815     hasPosition = false;
816     placement   = PS_INVALID;
817     layer       = LYR_INVALID;
818 
819     parent = aParent;
820 }
821 
822 
~IDF3_COMPONENT()823 IDF3_COMPONENT::~IDF3_COMPONENT()
824 {
825     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcS = components.begin();
826     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcE = components.end();
827 
828     while( itcS != itcE )
829     {
830         delete *itcS;
831         ++itcS;
832     }
833 
834     components.clear();
835 
836     std::list< IDF_DRILL_DATA* >::iterator itdS = drills.begin();
837     std::list< IDF_DRILL_DATA* >::iterator itdE = drills.end();
838 
839     while( itdS != itdE )
840     {
841         delete *itdS;
842         ++itdS;
843     }
844 
845     drills.clear();
846 }
847 
848 
849 #ifndef DISABLE_IDF_OWNERSHIP
checkOwnership(int aSourceLine,const char * aSourceFunc)850 bool IDF3_COMPONENT::checkOwnership( int aSourceLine, const char* aSourceFunc )
851 {
852     if( !parent )
853     {
854         ostringstream ostr;
855         ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
856         ostr << "\n* BUG: parent not set";
857         errormsg = ostr.str();
858 
859         return false;
860     }
861 
862     IDF3::CAD_TYPE pcad = parent->GetCadType();
863 
864     switch( placement )
865     {
866         case PS_UNPLACED:
867         case PS_PLACED:
868         case PS_INVALID:
869             break;
870 
871         case PS_MCAD:
872 
873             if( pcad != CAD_MECH )
874             {
875                 ostringstream ostr;
876                 ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
877                 ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with "
878                         "PLACEMENT (";
879                 ostr << GetPlacementString( placement ) << ")";
880                 errormsg = ostr.str();
881 
882                 return false;
883             }
884 
885             break;
886 
887         case PS_ECAD:
888 
889             if( pcad != CAD_ELEC )
890             {
891                 ostringstream ostr;
892                 ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
893                 ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with "
894                         "PLACEMENT (";
895                 ostr << GetPlacementString( placement ) << ")";
896                 errormsg = ostr.str();
897 
898                 return false;
899             }
900             break;
901 
902         default:
903             do
904             {
905                 ostringstream ostr;
906                 ostr << "\n* BUG: unhandled internal placement value (" << placement << ")";
907                 errormsg = ostr.str();
908 
909                 return false;
910             } while( 0 );
911 
912             break;
913     }
914 
915     return true;
916 }
917 #endif
918 
919 
SetParent(IDF3_BOARD * aParent)920 void IDF3_COMPONENT::SetParent( IDF3_BOARD* aParent )
921 {
922     parent = aParent;
923     return;
924 }
925 
GetCadType(void)926 IDF3::CAD_TYPE IDF3_COMPONENT::GetCadType( void )
927 {
928     if( parent )
929         return parent->GetCadType();
930 
931     return CAD_INVALID;
932 }
933 
GetUnit(void)934 IDF3::IDF_UNIT IDF3_COMPONENT::GetUnit( void )
935 {
936     if( parent )
937         return parent->GetUnit();
938 
939     return UNIT_INVALID;
940 }
941 
SetRefDes(const std::string & aRefDes)942 bool IDF3_COMPONENT::SetRefDes( const std::string& aRefDes )
943 {
944 #ifndef DISABLE_IDF_OWNERSHIP
945     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
946         return false;
947 #endif
948 
949     if( aRefDes.empty() )
950     {
951         ostringstream ostr;
952         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid RefDes (empty)";
953         errormsg = ostr.str();
954 
955         return false;
956     }
957 
958     if( CompareToken( "PANEL", aRefDes ) )
959     {
960         ostringstream ostr;
961         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
962         ostr <<  "* BUG: PANEL is a reserved designator and may not be used by components";
963         errormsg = ostr.str();
964 
965         return false;
966     }
967 
968     refdes = aRefDes;
969     return true;
970 }
971 
972 
GetRefDes(void)973 const std::string& IDF3_COMPONENT::GetRefDes( void )
974 {
975     return refdes;
976 }
977 
978 
AddDrill(double aDia,double aXpos,double aYpos,IDF3::KEY_PLATING aPlating,const std::string & aHoleType,IDF3::KEY_OWNER aOwner)979 IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( double aDia, double aXpos, double aYpos,
980                                           IDF3::KEY_PLATING aPlating,
981                                           const std::string& aHoleType,
982                                           IDF3::KEY_OWNER aOwner )
983 {
984     IDF_DRILL_DATA* dp = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
985                                              refdes, aHoleType, aOwner );
986 
987     if( dp == nullptr )
988         return nullptr;
989 
990     drills.push_back( dp );
991 
992     return dp;
993 }
994 
995 
AddDrill(IDF_DRILL_DATA * aDrilledHole)996 IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( IDF_DRILL_DATA* aDrilledHole )
997 {
998     if( !aDrilledHole )
999         return nullptr;
1000 
1001     if( CompareToken( "PANEL", refdes ) )
1002     {
1003         ERROR_IDF;
1004         cerr << "\n* BUG: PANEL drills not supported at component level\n";
1005         return nullptr;
1006     }
1007 
1008     if( refdes.compare( aDrilledHole->GetDrillRefDes() ) )
1009     {
1010         ERROR_IDF;
1011         cerr << "\n* BUG: pushing an incorrect REFDES ('" << aDrilledHole->GetDrillRefDes();
1012         cerr << "') to component ('" << refdes << "')\n";
1013         return nullptr;
1014     }
1015 
1016     drills.push_back( aDrilledHole );
1017 
1018     return aDrilledHole;
1019 }
1020 
1021 
DelDrill(double aDia,double aXpos,double aYpos)1022 bool IDF3_COMPONENT::DelDrill( double aDia, double aXpos, double aYpos )
1023 {
1024 #ifndef DISABLE_IDF_OWNERSHIP
1025     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
1026         return false;
1027 #endif
1028 
1029     errormsg.clear();
1030 
1031     if( drills.empty() )
1032         return false;
1033 
1034     bool val = false;
1035 
1036     list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
1037     list< IDF_DRILL_DATA* >::iterator itE = drills.end();
1038 
1039     while( !drills.empty() && itS != itE )
1040     {
1041         if( (*itS)->Matches( aDia, aXpos, aYpos ) )
1042         {
1043             val = true;
1044             delete *itS;
1045             itS = drills.erase( itS );
1046             continue;
1047         }
1048 
1049         ++itS;
1050     }
1051 
1052     return val;
1053 }
1054 
1055 
DelDrill(IDF_DRILL_DATA * aDrill)1056 bool IDF3_COMPONENT::DelDrill( IDF_DRILL_DATA* aDrill )
1057 {
1058 #ifndef DISABLE_IDF_OWNERSHIP
1059     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
1060         return false;
1061 #endif
1062 
1063     errormsg.clear();
1064 
1065     if( drills.empty() )
1066         return false;
1067 
1068     list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
1069     list< IDF_DRILL_DATA* >::iterator itE = drills.end();
1070 
1071     while( !drills.empty() && itS != itE )
1072     {
1073         if( *itS == aDrill )
1074         {
1075             delete *itS;
1076             drills.erase( itS );
1077             return true;
1078         }
1079 
1080         ++itS;
1081     }
1082 
1083     return false;
1084 }
1085 
1086 
GetDrills(void)1087 const std::list< IDF_DRILL_DATA* >*const IDF3_COMPONENT::GetDrills( void )
1088 {
1089     return &drills;
1090 }
1091 
AddOutlineData(IDF3_COMP_OUTLINE_DATA * aComponentOutline)1092 bool IDF3_COMPONENT::AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
1093 {
1094     if( aComponentOutline == nullptr )
1095     {
1096         ostringstream ostr;
1097         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ <<
1098                 "(): invalid aComponentOutline (nullptr)";
1099         errormsg = ostr.str();
1100 
1101         return false;
1102     }
1103 
1104 
1105     components.push_back( aComponentOutline );
1106 
1107     return true;
1108 }
1109 
1110 
DeleteOutlineData(IDF3_COMP_OUTLINE_DATA * aComponentOutline)1111 bool IDF3_COMPONENT::DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
1112 {
1113 #ifndef DISABLE_IDF_OWNERSHIP
1114     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
1115         return false;
1116 #endif
1117 
1118     if( components.empty() )
1119     {
1120         ostringstream ostr;
1121         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): component list is empty";
1122         errormsg = ostr.str();
1123 
1124         return false;
1125     }
1126 
1127     if( aComponentOutline == nullptr )
1128     {
1129         ostringstream ostr;
1130         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ <<
1131                 "(): invalid aComponentOutline (nullptr)";
1132         errormsg = ostr.str();
1133 
1134         return false;
1135     }
1136 
1137     errormsg.clear();
1138 
1139     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
1140     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
1141 
1142     while( itS != itE )
1143     {
1144         if( *itS == aComponentOutline )
1145         {
1146             delete *itS;
1147             components.erase( itS );
1148             return true;
1149         }
1150 
1151         ++itS;
1152     }
1153 
1154     return false;
1155 }
1156 
1157 
DeleteOutlineData(size_t aIndex)1158 bool IDF3_COMPONENT::DeleteOutlineData( size_t aIndex )
1159 {
1160 #ifndef DISABLE_IDF_OWNERSHIP
1161     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
1162         return false;
1163 #endif
1164 
1165     if( aIndex >= components.size() )
1166     {
1167         ostringstream ostr;
1168         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1169         ostr << "* aIndex (" << aIndex << ") out of range; list size is " << components.size();
1170         errormsg = ostr.str();
1171 
1172         return false;
1173     }
1174 
1175     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
1176     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
1177     size_t idx = 0;
1178 
1179     while( itS != itE )
1180     {
1181         if( idx == aIndex )
1182         {
1183             delete *itS;
1184             components.erase( itS );
1185             return true;
1186         }
1187 
1188         ++idx;
1189         ++itS;
1190     }
1191 
1192     return false;
1193 }
1194 
1195 
GetOutlinesSize(void)1196 size_t IDF3_COMPONENT::GetOutlinesSize( void )
1197 {
1198     return components.size();
1199 }
1200 
1201 
GetOutlinesData(void)1202 const std::list< IDF3_COMP_OUTLINE_DATA* >*const IDF3_COMPONENT::GetOutlinesData( void )
1203 {
1204     return &components;
1205 }
1206 
1207 
GetPosition(double & aXpos,double & aYpos,double & aAngle,IDF3::IDF_LAYER & aLayer)1208 bool IDF3_COMPONENT::GetPosition( double& aXpos, double& aYpos, double& aAngle,
1209                                   IDF3::IDF_LAYER& aLayer )
1210 {
1211     errormsg.clear();
1212 
1213     if( !hasPosition )
1214     {
1215         aXpos = 0.0;
1216         aYpos = 0.0;
1217         aAngle = 0.0;
1218         aLayer = IDF3::LYR_INVALID;
1219         return false;
1220     }
1221 
1222     aXpos = xpos;
1223     aYpos = ypos;
1224     aAngle = angle;
1225     aLayer = layer;
1226     return true;
1227 }
1228 
1229 
SetPosition(double aXpos,double aYpos,double aAngle,IDF3::IDF_LAYER aLayer)1230 bool IDF3_COMPONENT::SetPosition( double aXpos, double aYpos, double aAngle,
1231                                   IDF3::IDF_LAYER aLayer )
1232 {
1233 #ifndef DISABLE_IDF_OWNERSHIP
1234     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
1235         return false;
1236 #endif
1237 
1238     errormsg.clear();
1239 
1240     switch( aLayer )
1241     {
1242         case LYR_TOP:
1243         case LYR_BOTTOM:
1244             break;
1245 
1246         default:
1247             do
1248             {
1249                 ostringstream ostr;
1250                 ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1251                 ostr << "\n* invalid side (must be TOP or BOTTOM only): " <<
1252                         GetLayerString( aLayer );
1253                 errormsg = ostr.str();
1254 
1255                 return false;
1256             } while( 0 );
1257 
1258             break;
1259     }
1260 
1261     if( hasPosition )
1262         return false;
1263 
1264     hasPosition = true;
1265     xpos = aXpos;
1266     ypos = aYpos;
1267     angle = aAngle;
1268     layer = aLayer;
1269     return true;
1270 }
1271 
1272 
GetPlacement(void)1273 IDF3::IDF_PLACEMENT IDF3_COMPONENT::GetPlacement( void )
1274 {
1275     return placement;
1276 }
1277 
1278 
SetPlacement(IDF3::IDF_PLACEMENT aPlacementValue)1279 bool IDF3_COMPONENT::SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue )
1280 {
1281     if( aPlacementValue < PS_UNPLACED || aPlacementValue >= PS_INVALID )
1282     {
1283         ostringstream ostr;
1284         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1285         ostr << "\n* invalid PLACEMENT value (" << aPlacementValue << ")";
1286         errormsg = ostr.str();
1287 
1288         return false;
1289     }
1290 
1291 #ifndef DISABLE_IDF_OWNERSHIP
1292     if( !checkOwnership( __LINE__, __FUNCTION__ ) )
1293         return false;
1294 #endif
1295 
1296     placement = aPlacementValue;
1297 
1298     return true;
1299 }
1300 
1301 
writeDrillData(std::ostream & aBoardFile)1302 bool IDF3_COMPONENT::writeDrillData( std::ostream& aBoardFile )
1303 {
1304     if( drills.empty() )
1305         return true;
1306 
1307     std::list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
1308     std::list< IDF_DRILL_DATA* >::iterator itE = drills.end();
1309 
1310     while( itS != itE )
1311     {
1312         (*itS)->write( aBoardFile, GetUnit() );
1313         ++itS;
1314     }
1315 
1316     return true;
1317 }
1318 
1319 
writePlaceData(std::ostream & aBoardFile)1320 bool IDF3_COMPONENT::writePlaceData( std::ostream& aBoardFile )
1321 {
1322     if( components.empty() )
1323         return true;
1324 
1325     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
1326     std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
1327 
1328     while( itS != itE )
1329     {
1330         (*itS)->writePlaceData( aBoardFile, xpos, ypos, angle, refdes, placement, layer );
1331         ++itS;
1332     }
1333 
1334     return true;
1335 }
1336 
1337 
IDF3_BOARD(IDF3::CAD_TYPE aCadType)1338 IDF3_BOARD::IDF3_BOARD( IDF3::CAD_TYPE aCadType )
1339 {
1340     idfVer         = IDF_V3;
1341     cadType        = aCadType;
1342     userPrec       = 5;
1343     userScale      = 1.0;
1344     userXoff       = 0.0;
1345     userYoff       = 0.0;
1346     brdFileVersion = 0;
1347     libFileVersion = 0;
1348     iRefDes        = 0;
1349     unit           = UNIT_MM;
1350 
1351     // unlike other outlines which are created as necessary,
1352     // the board outline always exists and its parent must
1353     // be set here
1354     olnBoard.setParent( this );
1355     olnBoard.setThickness( 1.6 );
1356 
1357     return;
1358 }
1359 
1360 
~IDF3_BOARD()1361 IDF3_BOARD::~IDF3_BOARD()
1362 {
1363     Clear();
1364 
1365     return;
1366 }
1367 
1368 
GetNewRefDes(void)1369 const std::string& IDF3_BOARD::GetNewRefDes( void )
1370 {
1371     ostringstream ostr;
1372     ostr << "NOREFDESn" << iRefDes++;
1373 
1374     sRefDes = ostr.str();
1375 
1376     return sRefDes;
1377 }
1378 
1379 
1380 #ifndef DISABLE_IDF_OWNERSHIP
checkComponentOwnership(int aSourceLine,const char * aSourceFunc,IDF3_COMPONENT * aComponent)1381 bool IDF3_BOARD::checkComponentOwnership( int aSourceLine, const char* aSourceFunc,
1382                                           IDF3_COMPONENT* aComponent )
1383 {
1384     if( !aComponent )
1385     {
1386         ostringstream ostr;
1387         ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc;
1388         ostr << "(): Invalid component pointer (nullptr)";
1389         errormsg = ostr.str();
1390 
1391         return false;
1392     }
1393 
1394     IDF3::IDF_PLACEMENT place = aComponent->GetPlacement();
1395 
1396     if( place == PS_PLACED || place == PS_UNPLACED )
1397         return true;
1398 
1399     if( place == PS_MCAD && cadType == CAD_MECH )
1400         return true;
1401 
1402     if( place == PS_ECAD && cadType == CAD_ELEC )
1403         return true;
1404 
1405     do
1406     {
1407         ostringstream ostr;
1408         ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
1409         ostr << "* ownership violation; CAD type is ";
1410 
1411         if( cadType == CAD_MECH )
1412             ostr << "MCAD ";
1413         else
1414             ostr << "ECAD ";
1415 
1416         ostr << "while outline owner is " << GetPlacementString( place ) << "\n";
1417         errormsg = ostr.str();
1418 
1419     } while( 0 );
1420 
1421     return false;
1422 }
1423 #endif
1424 
1425 
GetCadType(void)1426 IDF3::CAD_TYPE IDF3_BOARD::GetCadType( void )
1427 {
1428     return cadType;
1429 }
1430 
1431 
SetBoardName(const std::string & aBoardName)1432 void IDF3_BOARD::SetBoardName( const std::string& aBoardName )
1433 {
1434     boardName = std::move( aBoardName );
1435 }
1436 
1437 
GetBoardName(void)1438 const std::string& IDF3_BOARD::GetBoardName( void )
1439 {
1440     return boardName;
1441 }
1442 
1443 
setUnit(IDF3::IDF_UNIT aUnit,bool convert)1444 bool IDF3_BOARD::setUnit( IDF3::IDF_UNIT aUnit, bool convert )
1445 {
1446     switch( aUnit )
1447     {
1448     case UNIT_MM:
1449     case UNIT_THOU:
1450         unit = aUnit;
1451         break;
1452 
1453     case UNIT_TNM:
1454         ERROR_IDF << "\n* TNM unit is not supported; defaulting to mm\n";
1455         unit = UNIT_MM;
1456         break;
1457 
1458     default:
1459         do
1460         {
1461             ostringstream ostr;
1462             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1463             ostr << "* invalid board unit (" << aUnit << ")";
1464             errormsg = ostr.str();
1465 
1466             return false;
1467         } while( 0 );
1468 
1469         break;
1470     }
1471 
1472     // iterate through all owned OUTLINE objects (except IDF3_COMP_OUTLINE)
1473     // and set to the same unit
1474 
1475     olnBoard.SetUnit( aUnit );
1476 
1477     do
1478     {
1479         std::map< std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
1480         std::map< std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
1481 
1482         while( its != ite )
1483         {
1484             its->second->SetUnit( aUnit );
1485             ++its;
1486         }
1487 
1488     } while( 0 );
1489 
1490     do
1491     {
1492         std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
1493         std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
1494 
1495         while( its != ite )
1496         {
1497             (*its)->SetUnit( aUnit );
1498             ++its;
1499         }
1500 
1501     } while( 0 );
1502 
1503     do
1504     {
1505         std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
1506         std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
1507 
1508         while( its != ite )
1509         {
1510             (*its)->SetUnit( aUnit );
1511             ++its;
1512         }
1513 
1514     } while( 0 );
1515 
1516     do
1517     {
1518         std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
1519         std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
1520 
1521         while( its != ite )
1522         {
1523             (*its)->SetUnit( aUnit );
1524             ++its;
1525         }
1526 
1527     } while( 0 );
1528 
1529     do
1530     {
1531         std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
1532         std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
1533 
1534         while( its != ite )
1535         {
1536             (*its)->SetUnit( aUnit );
1537             ++its;
1538         }
1539 
1540     } while( 0 );
1541 
1542     do
1543     {
1544         std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
1545         std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
1546 
1547         while( its != ite )
1548         {
1549             (*its)->SetUnit( aUnit );
1550             ++its;
1551         }
1552 
1553     } while( 0 );
1554 
1555     do
1556     {
1557         std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
1558         std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
1559 
1560         while( its != ite )
1561         {
1562             its->second->SetUnit( aUnit );
1563             ++its;
1564         }
1565 
1566     } while( 0 );
1567 
1568     //iterate through all owned IDF3_COMP_OUTLINE objects and
1569     // set to the same unit IF convert = true
1570     if( convert )
1571     {
1572         std::map<std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
1573         std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
1574 
1575         while( its != ite )
1576         {
1577             its->second->SetUnit( aUnit );
1578             ++its;
1579         }
1580 
1581     }
1582 
1583     return true;
1584 }
1585 
1586 
GetUnit(void)1587 IDF3::IDF_UNIT IDF3_BOARD::GetUnit( void )
1588 {
1589     return unit;
1590 }
1591 
1592 
SetBoardThickness(double aBoardThickness)1593 bool IDF3_BOARD::SetBoardThickness( double aBoardThickness )
1594 {
1595     if( aBoardThickness <= 0.0 )
1596     {
1597         ostringstream ostr;
1598         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
1599         ostr << "board thickness (" << aBoardThickness << ") must be > 0";
1600         errormsg = ostr.str();
1601 
1602         return false;
1603     }
1604 
1605     if( !olnBoard.SetThickness( aBoardThickness ) )
1606     {
1607         errormsg = olnBoard.GetError();
1608         return false;
1609     }
1610 
1611     return true;
1612 }
1613 
1614 
GetBoardThickness(void)1615 double IDF3_BOARD::GetBoardThickness( void )
1616 {
1617     return olnBoard.GetThickness();
1618 }
1619 
1620 
readBrdDrills(std::istream & aBoardFile,IDF3::FILE_STATE & aBoardState)1621 void IDF3_BOARD::readBrdDrills( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState )
1622 {
1623     IDF_DRILL_DATA drill;
1624 
1625     while( drill.read( aBoardFile, unit, aBoardState, idfVer ) )
1626     {
1627         IDF_DRILL_DATA *dp = new IDF_DRILL_DATA;
1628         *dp = drill;
1629 
1630         if( AddDrill( dp ) == nullptr )
1631         {
1632             delete dp;
1633 
1634             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1635                               "\n* BUG: could not add drill data; cannot continue reading the "
1636                               "file" ) );
1637         }
1638     }
1639 }
1640 
1641 
readBrdNotes(std::istream & aBoardFile,IDF3::FILE_STATE & aBoardState)1642 void IDF3_BOARD::readBrdNotes( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState )
1643 {
1644     IDF_NOTE note;
1645 
1646     while( note.readNote( aBoardFile, aBoardState, unit ) )
1647     {
1648         IDF_NOTE *np = new IDF_NOTE;
1649         *np = note;
1650         notes.push_back( np );
1651     }
1652 }
1653 
1654 
readBrdPlacement(std::istream & aBoardFile,IDF3::FILE_STATE & aBoardState,bool aNoSubstituteOutlines)1655 void IDF3_BOARD::readBrdPlacement( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState,
1656                                    bool aNoSubstituteOutlines )
1657 {
1658     IDF3_COMP_OUTLINE_DATA oldata;
1659 
1660     while( oldata.readPlaceData( aBoardFile, aBoardState, this, idfVer, aNoSubstituteOutlines ) );
1661 }
1662 
1663 
readBrdHeader(std::istream & aBoardFile,IDF3::FILE_STATE & aBoardState)1664 void IDF3_BOARD::readBrdHeader( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState )
1665 {
1666     std::string iline;      // the input line
1667     bool isComment;         // true if a line just read in is a comment line
1668     std::streampos pos;
1669     int idx = 0;
1670     bool quoted = false;
1671     std::string token;
1672 
1673     // RECORD 1: ".HEADER" must be the very first line
1674     while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
1675 
1676     if( !aBoardFile.good() )
1677         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1678                           "problems reading board header" ) );
1679 
1680     if( isComment )
1681         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1682                           "invalid IDF file\n"
1683                           "* Violation of specification: first line must be .HEADER\n" ) );
1684 
1685     if( !CompareToken( ".HEADER", iline ) )
1686         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1687                           "invalid IDF file\n"
1688                           "* Violation of specification:\n"
1689                           "* first line must be .HEADER and have no quotes or trailing text" ) );
1690 
1691     // RECORD 2:
1692     //      File Type [str]: BOARD_FILE (PANEL_FILE not supported)
1693     //      IDF Version Number [float]: must be 3.0
1694     //      Source System [str]: ignored
1695     //      Date [str]: ignored
1696     //      Board File Version [int]: ignored
1697     while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
1698 
1699     if( !aBoardFile.good() )
1700         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1701                           "problems reading board header, RECORD 2" ) );
1702 
1703     if( isComment )
1704         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1705                           "invalid IDF file\n"
1706                           "* Violation of specification: comment within .HEADER section" ) );
1707 
1708     idx = 0;
1709     GetIDFString( iline, token, quoted, idx );
1710 
1711     if( quoted )
1712         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1713                           "invalid IDF file\n"
1714                           "* Violation of specification:\n"
1715                           "* File Type in HEADER section must not be in quotes" ) );
1716 
1717     if( !CompareToken( "BOARD_FILE", token ) )
1718     {
1719         ERROR_IDF;
1720 
1721         if( CompareToken( "PANEL_FILE", token ) )
1722             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1723                               "not a board file\n"
1724                               "* PANEL_FILE is not supported (expecting BOARD_FILE)" ) );
1725         else
1726             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1727                               "invalid IDF file\n"
1728                               "* Expecting string: BOARD_FILE" ) );
1729     }
1730 
1731     if( !GetIDFString( iline, token, quoted, idx ) )
1732         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1733                           "invalid IDF file\n"
1734                           "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
1735 
1736     if( quoted )
1737         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1738                           "invalid IDF file\n"
1739                           "* Violation of specification: IDF Version must not be in quotes" ) );
1740 
1741     if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
1742     {
1743         idfVer = IDF_V3;
1744     }
1745     else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
1746     {
1747         idfVer = IDF_V2;
1748     }
1749     else
1750     {
1751         ostringstream ostr;
1752 
1753         ostr << "unsupported IDF version\n";
1754         ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
1755 
1756         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1757     }
1758 
1759     if( !GetIDFString( iline, token, quoted, idx ) )
1760         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1761                           "invalid IDF file\n"
1762                           "* Violation of specification:\n"
1763                           "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
1764 
1765     brdSource = token;
1766 
1767     if( !GetIDFString( iline, token, quoted, idx ) )
1768         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1769                           "invalid IDF file\n"
1770                           "* Violation of specification:\n"
1771                           "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
1772 
1773     brdDate = token;
1774 
1775     if( !GetIDFString( iline, token, quoted, idx ) )
1776         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1777                           "invalid IDF file\n"
1778                           "* Violation of specification:\n"
1779                           "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
1780 
1781     std::istringstream istr;
1782     istr.str( token );
1783 
1784     istr >> brdFileVersion;
1785 
1786     if( istr.fail() )
1787     {
1788         ERROR_IDF << "invalid Board File Version in header\n";
1789         cerr << "* Setting default version of 1\n";
1790         brdFileVersion = 1;
1791     }
1792 
1793     if( quoted )
1794         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1795                           "invalid IDF file\n"
1796                           "* Violation of specification:\n"
1797                           "* HEADER section, RECORD 2, FIELD 5: Board File Version must not be "
1798                           "in quotes" ) );
1799 
1800     // RECORD 3:
1801     //      Board Name [str]: stored
1802     //      Units [str]: MM or THOU
1803     while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
1804 
1805     if( !aBoardFile.good() )
1806         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1807                           "\n* problems reading board header, RECORD 2" ) );
1808 
1809     if( isComment )
1810         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1811                           "invalid IDF file\n"
1812                           "* Violation of specification: comment within .HEADER section" ) );
1813 
1814     idx = 0;
1815     GetIDFString( iline, token, quoted, idx );
1816 
1817     boardName = token;
1818 
1819     if( !GetIDFString( iline, token, quoted, idx ) )
1820         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1821                           "invalid IDF file\n"
1822                           "* Violation of specification:\n"
1823                           "* HEADER section, RECORD 3, FIELD 1: no Board Name" ) );
1824 
1825     if( quoted )
1826         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1827                           "invalid IDF file\n"
1828                           "* Violation of specification:\n"
1829                           "* HEADER section, RECORD 3, FIELD 2: UNIT may not be in quotes" ) );
1830 
1831     if( CompareToken( "MM", token ) )
1832     {
1833         unit = IDF3::UNIT_MM;
1834     }
1835     else if( CompareToken( "THOU", token ) )
1836     {
1837         unit = IDF3::UNIT_THOU;
1838     }
1839     else if( ( idfVer == IDF_V2 ) && CompareToken( "TNM", token ) )
1840     {
1841         unit = IDF3::UNIT_TNM;
1842     }
1843     else
1844     {
1845         ostringstream ostr;
1846 
1847         ostr << "invalid IDF file\n";
1848         ostr << "* HEADER section, RECORD 3, FIELD 2: expecting MM or THOU (got '" << token <<
1849                 "')\n";
1850 
1851         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1852     }
1853 
1854     olnBoard.SetUnit( unit );
1855 
1856     // RECORD 4:
1857     //      .END_HEADER
1858     while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
1859 
1860     if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
1861         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1862                           "problems reading board header, RECORD 4" ) );
1863 
1864     if( isComment )
1865         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1866                           "invalid IDF file\n"
1867                           "* Violation of specification: comment within .HEADER section\n" ) );
1868 
1869     if( !CompareToken( ".END_HEADER", iline ) )
1870     {
1871         ostringstream ostr;
1872 
1873         ostr << "invalid IDF file\n";
1874         ostr << "* Violation of specification: expected .END_HEADER\n";
1875         ostr << "* line: '" << iline << "'";
1876 
1877         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1878     }
1879 
1880     aBoardState = IDF3::FILE_HEADER;
1881 }
1882 
1883 
readBrdSection(std::istream & aBoardFile,IDF3::FILE_STATE & aBoardState,bool aNoSubstituteOutlines)1884 void IDF3_BOARD::readBrdSection( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState,
1885                                  bool aNoSubstituteOutlines )
1886 {
1887     std::list< std::string > comments;  // comments associated with a section
1888 
1889     // Reads in .SECTION_ID or #COMMENTS
1890     // Expected SECTION IDs:
1891     //      .BOARD_OUTLINE
1892     //      .PANEL_OUTLINE (NOT SUPPORTED)
1893     //      .OTHER_OUTLINE
1894     //      .ROUTE_OUTLINE
1895     //      .PLACE_OUTLINE
1896     //      .ROUTE_KEEPOUT
1897     //      .VIA_KEEPOUT
1898     //      .PLACE_KEEPOUT
1899     //      .PLACE_REGION
1900     //      .DRILLED_HOLES
1901     //      .NOTES
1902     //      .PLACEMENT
1903     std::string iline;      // the input line
1904     bool isComment;         // true if a line just read in is a comment line
1905     std::streampos pos;
1906     int idx = 0;
1907     bool quoted = false;
1908     std::string token;
1909 
1910     while( aBoardFile.good() )
1911     {
1912         while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
1913 
1914         if( !aBoardFile.good() )
1915         {
1916             if( aBoardFile.eof() && aBoardState >= IDF3::FILE_HEADER &&
1917                 aBoardState < IDF3::FILE_INVALID )
1918             {
1919                 if( !comments.empty() )
1920                     ERROR_IDF << "[warning]: trailing comments in IDF file (comments will be "
1921                                  "lost)\n";
1922 
1923                 return;
1924             }
1925 
1926             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1927                               "problems reading board section" ) );
1928         }
1929 
1930         if( isComment )
1931         {
1932             comments.push_back( iline );
1933             continue;
1934         }
1935 
1936         // This must be a header
1937         GetIDFString( iline, token, quoted, idx );
1938 
1939         if( quoted )
1940         {
1941             ostringstream ostr;
1942 
1943             ostr << "invalid IDF file\n";
1944             ostr << "* Violation of specification: quoted string where SECTION HEADER expected\n";
1945             ostr << "* line: '" << iline << "'";
1946             ostr << "* position: " << pos;
1947 
1948             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1949         }
1950 
1951         if( CompareToken( ".BOARD_OUTLINE", token ) )
1952         {
1953             if( aBoardState != IDF3::FILE_HEADER )
1954             {
1955                 aBoardState = IDF3::FILE_INVALID;
1956                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1957                                   "invalid IDF file\n"
1958                                   "* Violation of specification: no HEADER section" ) );
1959             }
1960 
1961             olnBoard.readData( aBoardFile, iline, idfVer );
1962 
1963             if( !comments.empty() )
1964             {
1965                 std::list<std::string>::iterator its = comments.begin();
1966                 std::list<std::string>::iterator ite = comments.end();
1967 
1968                 while( its != ite )
1969                 {
1970                     olnBoard.AddComment( *its );
1971                     ++its;
1972                 }
1973             }
1974 
1975             aBoardState = IDF3::FILE_OUTLINE;
1976             return;
1977         }
1978 
1979         if( CompareToken( ".PANEL_OUTLINE", token ) )
1980             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1981                               "PANEL_OUTLINE not supported" ) );
1982 
1983         if( CompareToken( ".OTHER_OUTLINE", token ) )
1984         {
1985             if( aBoardState != IDF3::FILE_OUTLINE )
1986                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1987                                   "invalid IDF file\n"
1988                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
1989                                   ".OTHER_OUTLINE" ) );
1990 
1991             OTHER_OUTLINE* op = new OTHER_OUTLINE( this );
1992 
1993             if( op == nullptr )
1994                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1995                                   "could not create OTHER_OUTLINE object" ) );
1996 
1997             op->SetUnit( unit );
1998             op->readData( aBoardFile, iline, idfVer );
1999 
2000             if( !comments.empty() )
2001             {
2002                 std::list<std::string>::iterator its = comments.begin();
2003                 std::list<std::string>::iterator ite = comments.end();
2004 
2005                 while( its != ite )
2006                 {
2007                     op->AddComment( *its );
2008                     ++its;
2009                 }
2010             }
2011 
2012             if( olnOther.insert( pair<string,
2013                                  OTHER_OUTLINE*>(op->GetOutlineIdentifier(), op ) ).second == false )
2014             {
2015                 ostringstream ostr;
2016                 ostr << "invalid IDF file\n";
2017                 ostr << "* Violation of specification. Non-unique ID in OTHER_OUTLINE '";
2018                 ostr << op->GetOutlineIdentifier() << "'\n";
2019                 ostr << "* line: '" << iline << "'\n";
2020                 ostr << "* pos: " << pos;
2021                 delete op;
2022 
2023                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2024             }
2025 
2026             return;
2027         }
2028 
2029         if( CompareToken( ".ROUTE_OUTLINE", token ) )
2030         {
2031             if( aBoardState != IDF3::FILE_OUTLINE )
2032                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2033                                   "invalid IDF file\n"
2034                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2035                                   ".ROUTE_OUTLINE" ) );
2036 
2037             ROUTE_OUTLINE* op = new ROUTE_OUTLINE( this );
2038 
2039             if( op == nullptr )
2040                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2041                                   "could not create ROUTE_OUTLINE object" ) );
2042 
2043             op->SetUnit( unit );
2044             op->readData( aBoardFile, iline, idfVer );
2045 
2046             if( !comments.empty() )
2047             {
2048                 std::list<std::string>::iterator its = comments.begin();
2049                 std::list<std::string>::iterator ite = comments.end();
2050 
2051                 while( its != ite )
2052                 {
2053                     op->AddComment( *its );
2054                     ++its;
2055                 }
2056             }
2057 
2058             olnRoute.push_back( op );
2059 
2060             return;
2061         }
2062 
2063         if( CompareToken( ".PLACE_OUTLINE", token ) )
2064         {
2065             if( aBoardState != IDF3::FILE_OUTLINE )
2066                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2067                                   "invalid IDF file\n"
2068                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2069                                   ".PLACE_OUTLINE" ) );
2070 
2071             PLACE_OUTLINE* op = new PLACE_OUTLINE( this );
2072 
2073             if( op == nullptr )
2074                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2075                                   "could not create PLACE_OUTLINE object" ) );
2076 
2077             op->SetUnit( unit );
2078             op->readData( aBoardFile, iline, idfVer );
2079 
2080             if( !comments.empty() )
2081             {
2082                 std::list<std::string>::iterator its = comments.begin();
2083                 std::list<std::string>::iterator ite = comments.end();
2084 
2085                 while( its != ite )
2086                 {
2087                     op->AddComment( *its );
2088                     ++its;
2089                 }
2090             }
2091 
2092             olnPlace.push_back( op );
2093 
2094             return;
2095         }
2096 
2097         if( CompareToken( ".ROUTE_KEEPOUT", token ) )
2098         {
2099             if( aBoardState != IDF3::FILE_OUTLINE )
2100                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2101                                   "invalid IDF file\n"
2102                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2103                                   ".ROUTE_KEEPOUT" ) );
2104 
2105             ROUTE_KO_OUTLINE* op = new ROUTE_KO_OUTLINE( this );
2106 
2107             if( op == nullptr )
2108                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2109                                   "could not create ROUTE_KEEPOUT object" ) );
2110 
2111             op->SetUnit( unit );
2112             op->readData( aBoardFile, iline, idfVer );
2113 
2114             if( !comments.empty() )
2115             {
2116                 std::list<std::string>::iterator its = comments.begin();
2117                 std::list<std::string>::iterator ite = comments.end();
2118 
2119                 while( its != ite )
2120                 {
2121                     op->AddComment( *its );
2122                     ++its;
2123                 }
2124             }
2125 
2126             olnRouteKeepout.push_back( op );
2127 
2128             return;
2129         }
2130 
2131         if( CompareToken( ".VIA_KEEPOUT", token ) )
2132         {
2133             if( aBoardState != IDF3::FILE_OUTLINE )
2134                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2135                                   "invalid IDF file\n"
2136                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2137                                   ".VIA_KEEPOUT" ) );
2138 
2139             VIA_KO_OUTLINE* op = new VIA_KO_OUTLINE( this );
2140 
2141             if( op == nullptr )
2142                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2143                                   "could not create VIA_KEEPOUT object" ) );
2144 
2145             op->SetUnit( unit );
2146             op->readData( aBoardFile, iline, idfVer );
2147 
2148             if( !comments.empty() )
2149             {
2150                 std::list<std::string>::iterator its = comments.begin();
2151                 std::list<std::string>::iterator ite = comments.end();
2152 
2153                 while( its != ite )
2154                 {
2155                     op->AddComment( *its );
2156                     ++its;
2157                 }
2158             }
2159 
2160             olnViaKeepout.push_back( op );
2161 
2162             return;
2163         }
2164 
2165         if( CompareToken( ".PLACE_KEEPOUT", token ) )
2166         {
2167             if( aBoardState != IDF3::FILE_OUTLINE )
2168                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2169                                   "invalid IDF file\n"
2170                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2171                                   ".PLACE_KEEPOUT" ) );
2172 
2173             PLACE_KO_OUTLINE* op = new PLACE_KO_OUTLINE( this );
2174 
2175             if( op == nullptr )
2176                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2177                                   "could not create PLACE_KEEPOUT object" ) );
2178 
2179             op->SetUnit( unit );
2180             op->readData( aBoardFile, iline, idfVer );
2181 
2182             if( !comments.empty() )
2183             {
2184                 std::list<std::string>::iterator its = comments.begin();
2185                 std::list<std::string>::iterator ite = comments.end();
2186 
2187                 while( its != ite )
2188                 {
2189                     op->AddComment( *its );
2190                     ++its;
2191                 }
2192             }
2193 
2194             olnPlaceKeepout.push_back( op );
2195 
2196             return;
2197         }
2198 
2199         if( CompareToken( ".PLACE_REGION", token ) )
2200         {
2201             if( aBoardState != IDF3::FILE_OUTLINE )
2202                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2203                                   "invalid IDF file\n"
2204                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2205                                   ".PLACE_REGION" ) );
2206 
2207             GROUP_OUTLINE* op = new GROUP_OUTLINE( this );
2208 
2209             if( op == nullptr )
2210                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2211                                   "could not create PLACE_REGION object" ) );
2212 
2213             op->SetUnit( unit );
2214             op->readData( aBoardFile, iline, idfVer );
2215 
2216             if( !comments.empty() )
2217             {
2218                 std::list<std::string>::iterator its = comments.begin();
2219                 std::list<std::string>::iterator ite = comments.end();
2220 
2221                 while( its != ite )
2222                 {
2223                     op->AddComment( *its );
2224                     ++its;
2225                 }
2226             }
2227 
2228             olnGroup.insert( pair<string, GROUP_OUTLINE*>(op->GetGroupName(), op) );
2229 
2230             return;
2231         }
2232 
2233         if( CompareToken( ".DRILLED_HOLES", token ) )
2234         {
2235             if( aBoardState != IDF3::FILE_OUTLINE )
2236                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2237                                   "invalid IDF file\n"
2238                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2239                                   ".DRILLED_HOLES" ) );
2240 
2241             readBrdDrills( aBoardFile, aBoardState );
2242 
2243             if( !comments.empty() )
2244             {
2245                 std::list<std::string>::iterator its = comments.begin();
2246                 std::list<std::string>::iterator ite = comments.end();
2247 
2248                 while( its != ite )
2249                 {
2250                     drillComments.push_back( *its );
2251                     ++its;
2252                 }
2253             }
2254 
2255             return;
2256         }
2257 
2258         if( CompareToken( ".NOTES", token ) )
2259         {
2260             if( aBoardState != IDF3::FILE_OUTLINE )
2261                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2262                                   "invalid IDF file\n"
2263                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2264                                   ".NOTES" ) );
2265 
2266             if( idfVer < IDF_V3 )
2267                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2268                                   "invalid IDFv2 file\n"
2269                                   "* Violation of specification: NOTES section not in "
2270                                   "specification" ) );
2271 
2272             readBrdNotes( aBoardFile, aBoardState );
2273 
2274             if( !comments.empty() )
2275             {
2276                 std::list<std::string>::iterator its = comments.begin();
2277                 std::list<std::string>::iterator ite = comments.end();
2278 
2279                 while( its != ite )
2280                 {
2281                     noteComments.push_back( *its );
2282                     ++its;
2283                 }
2284             }
2285 
2286             return;
2287         }
2288 
2289         if( CompareToken( ".PLACEMENT", token ) )
2290         {
2291             if( aBoardState != IDF3::FILE_OUTLINE )
2292                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2293                                   "invalid IDF file\n"
2294                                   "* Violation of specification: expecting .BOARD_OUTLINE, have "
2295                                   ".PLACEMENT" ) );
2296 
2297             readBrdPlacement( aBoardFile, aBoardState, aNoSubstituteOutlines );
2298 
2299             if( !comments.empty() )
2300             {
2301                 std::list<std::string>::iterator its = comments.begin();
2302                 std::list<std::string>::iterator ite = comments.end();
2303 
2304                 while( its != ite )
2305                 {
2306                     placeComments.push_back( *its );
2307                     ++its;
2308                 }
2309             }
2310 
2311             return;
2312         }
2313     }
2314 }
2315 
2316 
readBoardFile(const std::string & aFileName,bool aNoSubstituteOutlines)2317 void IDF3_BOARD::readBoardFile( const std::string& aFileName, bool aNoSubstituteOutlines )
2318 {
2319     OPEN_ISTREAM( brd, aFileName.c_str() );
2320 
2321     try
2322     {
2323         brd.exceptions ( std::ios_base::badbit );
2324 
2325         if( brd.fail() )
2326         {
2327             ostringstream ostr;
2328             ostr << "\n* could not open file: '" << aFileName << "'";
2329 
2330             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2331         }
2332 
2333         brd.imbue( std::locale( "C" ) );
2334         std::string iline;      // the input line
2335         bool isComment;         // true if a line just read in is a comment line
2336         std::streampos pos;
2337         IDF3::FILE_STATE state = IDF3::FILE_START;
2338 
2339         // note: as per IDFv3 specification:
2340         //      "The Header section must be the first section in the file, the second
2341         //       section must be the Outline section, and the last section must be the
2342         //       Placement section. All other sections may be in any order."
2343 
2344         // further notes: Except for the HEADER section, sections may be preceded by
2345         // comment lines which will be copied back out on write(). No comments may
2346         // be associated with the board file itself since the only logical location
2347         // for unambiguous association is at the end of the file, which is inconvenient
2348         // for large files.
2349 
2350         readBrdHeader( brd, state );
2351 
2352         // read the various sections
2353         while( state != IDF3::FILE_PLACEMENT && brd.good() )
2354             readBrdSection( brd, state, aNoSubstituteOutlines );
2355 
2356         if( !brd.good() )
2357         {
2358             // check if we have valid data
2359             if( brd.eof() && state >= IDF3::FILE_OUTLINE && state < IDF3::FILE_INVALID )
2360             {
2361                 CLOSE_STREAM( brd );
2362                 return;
2363             }
2364 
2365             CLOSE_STREAM( brd );
2366             ostringstream ostr;
2367             ostr << "\n* empty IDF file: '" << aFileName << "'";
2368 
2369             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2370         }
2371 
2372         if( brd.good() && state == IDF3::FILE_PLACEMENT )
2373         {
2374             // read in any trailing lines and report on ignored comments (minor fault)
2375             // and any non-comment item (non-compliance with IDFv3)
2376             while( brd.good() )
2377             {
2378                 while( !FetchIDFLine( brd, iline, isComment, pos ) && brd.good() );
2379 
2380                 // normally this is a fault but we have all the data in accordance with specs
2381                 if( ( !brd.good() && !brd.eof() ) || iline.empty() )
2382                     break;
2383 
2384                 if( isComment )
2385                 {
2386                     ERROR_IDF << "[warning]: trailing comments after PLACEMENT\n";
2387                 }
2388                 else
2389                 {
2390                     ostringstream ostr;
2391                     ostr << "\n* problems reading file: '" << aFileName << "'";
2392 
2393                     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2394                                       "invalid IDF file\n"
2395                                       "* Violation of specification: non-comment lines after "
2396                                       "PLACEMENT section" ) );
2397                 }
2398             }
2399         }
2400     }
2401     catch( const std::exception& )
2402     {
2403         brd.exceptions ( std::ios_base::goodbit );
2404         CLOSE_STREAM( brd );
2405         throw;
2406     }
2407 
2408     CLOSE_STREAM( brd );
2409     return;
2410 }
2411 
2412 
readLibSection(std::istream & aLibFile,IDF3::FILE_STATE & aLibState,IDF3_BOARD * aBoard)2413 void IDF3_BOARD::readLibSection( std::istream& aLibFile, IDF3::FILE_STATE& aLibState,
2414                                  IDF3_BOARD* aBoard )
2415 {
2416     if( aBoard == nullptr )
2417     {
2418         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2419                           "\n* BUG: invoked with nullptr reference aBoard" ) );
2420     }
2421 
2422     std::list< std::string > comments;  // comments associated with a section
2423 
2424     // Reads in .ELECTRICAL, .MECHANICAL or #COMMENTS
2425     std::string iline;      // the input line
2426     bool isComment;         // true if a line just read in is a comment line
2427     std::streampos pos;
2428     int idx = 0;
2429     bool quoted = false;
2430     std::string token;
2431     IDF3_COMP_OUTLINE *pout = new IDF3_COMP_OUTLINE( this );
2432 
2433     if( !pout )
2434         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2435                           "\n* memory allocation failure" ) );
2436 
2437     while( aLibFile.good() )
2438     {
2439         while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
2440 
2441         if( !aLibFile.good() && !aLibFile.eof() )
2442         {
2443             delete pout;
2444             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2445                               "problems reading library section" ) );
2446         }
2447 
2448         // no data was read; this only happens at eof()
2449         if( iline.empty() )
2450         {
2451             delete pout;
2452             return;
2453         }
2454 
2455         if( isComment )
2456         {
2457             comments.push_back( iline );
2458             continue;
2459         }
2460 
2461         // This must be a header
2462         GetIDFString( iline, token, quoted, idx );
2463 
2464         if( quoted )
2465         {
2466             ostringstream ostr;
2467             ostr << "invalid IDF library\n";
2468             ostr << "* Violation of specification: quoted string where .ELECTRICAL or "
2469                     ".MECHANICAL expected\n";
2470             ostr << "* line: '" << iline << "'\n";
2471             ostr << "* pos: " << pos;
2472             delete pout;
2473 
2474             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2475         }
2476 
2477         if( CompareToken( ".ELECTRICAL", token ) || CompareToken( ".MECHANICAL", token ) )
2478         {
2479             pout->readData( aLibFile, token, idfVer );
2480 
2481             if( !comments.empty() )
2482             {
2483                 std::list<std::string>::iterator its = comments.begin();
2484                 std::list<std::string>::iterator ite = comments.end();
2485 
2486                 while( its != ite )
2487                 {
2488                     pout->AddComment( *its );
2489                     ++its;
2490                 }
2491             }
2492 
2493             IDF3_COMP_OUTLINE* cop = aBoard->GetComponentOutline( pout->GetUID() );
2494 
2495             if( cop == nullptr )
2496             {
2497                 compOutlines.insert( pair<const std::string,
2498                                      IDF3_COMP_OUTLINE*>( pout->GetUID(), pout ) );
2499             }
2500             else
2501             {
2502                 if( MatchCompOutline( pout, cop ) )
2503                 {
2504                     delete pout;
2505                     // everything is fine; the outlines are genuine duplicates
2506                     return;
2507                 }
2508 
2509                 ostringstream ostr;
2510                 ostr << "invalid IDF library\n";
2511                 ostr << "duplicate Component Outline: '" << pout->GetUID() << "'\n";
2512                 ostr << "* Violation of specification: multiple outlines have the same GEOM and "
2513                         "PART name\n";
2514                 ostr << "* line: '" << iline << "'\n";
2515                 ostr << "* pos: " << pos;
2516                 delete pout;
2517 
2518                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2519             }
2520 
2521             return;
2522         }
2523         else
2524         {
2525             ostringstream ostr;
2526             ostr << "invalid IDF library\n";
2527             ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << token << "'\n";
2528             ostr << "* line: '" << iline << "'\n";
2529             ostr << "* pos: " << pos;
2530             delete pout;
2531 
2532             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2533         }
2534     }
2535 
2536     delete pout;
2537 
2538     if( !aLibFile.eof() )
2539         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2540                           "problems reading IDF library file" ) );
2541 
2542     return;
2543 }
2544 
2545 
2546 // read the library HEADER
readLibHeader(std::istream & aLibFile,IDF3::FILE_STATE & aLibState)2547 void IDF3_BOARD::readLibHeader( std::istream& aLibFile, IDF3::FILE_STATE& aLibState )
2548 {
2549     std::string iline;      // the input line
2550     bool isComment;         // true if a line just read in is a comment line
2551     std::streampos pos;
2552     int idx = 0;
2553     bool quoted = false;
2554     std::string token;
2555 
2556     // RECORD 1: ".HEADER" must be the very first line
2557     while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
2558 
2559     if( !aLibFile.good() )
2560         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2561                           "invalid IDF library file\n"
2562                           "* premature end of file (no HEADER)" ) );
2563 
2564     if( isComment )
2565         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2566                           "invalid IDF library file\n"
2567                           "* Violation of specification: first line must be .HEADER" ) );
2568 
2569     if( !CompareToken( ".HEADER", iline ) )
2570         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2571                           "invalid IDF library file\n"
2572                           "* Violation of specification:\n"
2573                           "* first line must be .HEADER and have no quotes or trailing text" ) );
2574 
2575     // RECORD 2:
2576     //      File Type [str]: LIBRARY_FILE
2577     //      IDF Version Number [float]: must be 3.0
2578     //      Source System [str]: ignored
2579     //      Date [str]: ignored
2580     //      Library File Version [int]: ignored
2581     while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
2582 
2583     if( !aLibFile.good() )
2584         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2585                           "invalid IDF library file\n"
2586                           "* premature end of HEADER" ) );
2587 
2588     if( isComment )
2589         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2590                           "invalid IDF library file\n"
2591                           "* Violation of specification: comment within .HEADER section" ) );
2592 
2593     idx = 0;
2594     GetIDFString( iline, token, quoted, idx );
2595 
2596     if( quoted )
2597         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2598                           "invalid IDF library file\n"
2599                           "* Violation of specification:\n"
2600                           "* file Type in HEADER section must not be in quotes" ) );
2601 
2602     if( !CompareToken( "LIBRARY_FILE", token ) )
2603     {
2604         ostringstream ostr;
2605         ostr << "invalid IDF library\n";
2606         ostr << "* Expecting string: LIBRARY_FILE (got '" << token << "')\n";
2607 
2608         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2609     }
2610 
2611     if( !GetIDFString( iline, token, quoted, idx ) )
2612         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2613                           "invalid IDF library file\n"
2614                           "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
2615 
2616     if( quoted )
2617         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2618                           "invalid IDF library file\n"
2619                           "* Violation of specification: IDF Version must not be in quotes" ) );
2620 
2621     if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
2622     {
2623         idfVer = IDF_V3;
2624     }
2625     else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
2626     {
2627         idfVer = IDF_V2;
2628     }
2629     else
2630     {
2631         ostringstream ostr;
2632 
2633         ostr << "unsupported IDF version\n";
2634         ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
2635 
2636         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2637     }
2638 
2639     if( !GetIDFString( iline, token, quoted, idx ) )
2640         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2641                           "invalid IDF library file\n"
2642                           "* Violation of specification:\n"
2643                           "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
2644 
2645     libSource = token;
2646 
2647     if( !GetIDFString( iline, token, quoted, idx ) )
2648         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2649                           "invalid IDF library file\n"
2650                           "* Violation of specification:\n"
2651                           "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
2652 
2653     libDate = token;
2654 
2655     if( !GetIDFString( iline, token, quoted, idx ) )
2656         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2657                           "invalid IDF library file\n"
2658                           "* Violation of specification:\n"
2659                           "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
2660 
2661     std::istringstream istr;
2662     istr.str( token );
2663 
2664     istr >> libFileVersion;
2665 
2666     if( istr.fail() )
2667     {
2668         ERROR_IDF << "invalid Library File Version in header\n";
2669         cerr << "* Setting default version of 1\n";
2670         libFileVersion = 1;
2671     }
2672 
2673     if( quoted )
2674         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2675                           "invalid IDF library file\n"
2676                           "* Violation of specification:\n"
2677                           "* HEADER section, RECORD 2, FIELD 5: Library File Version must not "
2678                           "be in quotes" ) );
2679 
2680     // RECORD 3:
2681     //      .END_HEADER
2682     while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
2683 
2684     if( ( !aLibFile.good() && !aLibFile.eof() ) || iline.empty() )
2685         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2686                           "problems reading library header, RECORD 3" ) );
2687 
2688     if( isComment )
2689         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2690                           "invalid IDF library file\n"
2691                           "* Violation of specification: comment within .HEADER section" ) );
2692 
2693     if( !CompareToken( ".END_HEADER", iline ) )
2694     {
2695         ostringstream ostr;
2696         ostr << "invalid IDF header\n";
2697         ostr << "* Violation of specification: expected .END_HEADER (got '" << iline << "')\n";
2698 
2699         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2700     }
2701 
2702     aLibState = IDF3::FILE_HEADER;
2703 }
2704 
2705 
readLibFile(const std::string & aFileName)2706 void IDF3_BOARD::readLibFile( const std::string& aFileName )
2707 {
2708     OPEN_ISTREAM( lib, aFileName.c_str() );
2709 
2710     try
2711     {
2712         lib.exceptions ( std::ios_base::badbit );
2713 
2714         if( lib.fail() )
2715         {
2716             ostringstream ostr;
2717             ostr << "\n* could not open file: '" << aFileName << "'";
2718 
2719             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2720         }
2721 
2722         lib.imbue( std::locale( "C" ) );
2723         IDF3::FILE_STATE state = IDF3::FILE_START;
2724 
2725         readLibHeader( lib, state );
2726 
2727         while( lib.good() ) readLibSection( lib, state, this );
2728     }
2729     catch( const std::exception& )
2730     {
2731         lib.exceptions ( std::ios_base::goodbit );
2732         CLOSE_STREAM( lib );
2733         throw;
2734     }
2735 
2736     CLOSE_STREAM( lib );
2737 }
2738 
2739 
ReadFile(const wxString & aFullFileName,bool aNoSubstituteOutlines)2740 bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutlines )
2741 {
2742     // 1. Check that the file extension is 'emn'
2743     // 2. Check if a file with extension 'emp' exists and read it
2744     // 3. Open the specified filename and read it
2745 
2746     wxFileName brdname( aFullFileName );
2747     wxFileName libname( aFullFileName );
2748     wxString ext = brdname.GetExt();
2749 
2750     if( !ext.Cmp( "EMN" ) )
2751     {
2752         libname.SetExt( wxT( "EMP" ) );
2753     }
2754     else if( !ext.Cmp( "emn" ) )
2755     {
2756         libname.SetExt( wxT( "emp" ) );
2757     }
2758     else
2759     {
2760         ostringstream ostr;
2761         ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
2762         ostr << "* [INFO] invalid file name: '" << aFullFileName.ToUTF8() << "'";
2763 
2764         errormsg = ostr.str();
2765     }
2766 
2767 
2768     brdname.SetExt( wxT( "emn" ) );
2769 
2770     std::string bfname = TO_UTF8( aFullFileName );
2771 
2772     if( !wxFileExists( bfname ) )
2773     {
2774         brdname.SetExt( wxT( "EMN" ) );
2775         libname.SetExt( wxT( "EMP" ) );
2776     }
2777 
2778     try
2779     {
2780         if( !brdname.IsOk() )
2781         {
2782             ostringstream ostr;
2783             ostr << "\n* invalid file name: '" << aFullFileName.ToUTF8() << "'";
2784 
2785             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2786         }
2787 
2788         if( !brdname.FileExists() )
2789         {
2790             ostringstream ostr;
2791             ostr << "\n* no such file: '" << aFullFileName.ToUTF8() << "'";
2792 
2793             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2794         }
2795 
2796         if( !brdname.IsFileReadable() )
2797         {
2798             ostringstream ostr;
2799             ostr << "\n* cannot read file: '" << bfname << "'";
2800 
2801             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2802         }
2803 
2804         bfname = TO_UTF8( brdname.GetFullPath() );
2805         std::string lfname = TO_UTF8( libname.GetFullPath() );
2806 
2807         if( !libname.FileExists() )
2808         {
2809             // NOTE: Since this is a common case we simply proceed
2810             // with the assumption that there is no library file;
2811             // however we print a message to inform the user.
2812             ERROR_IDF;
2813             cerr << "no associated library file (*.emp)\n";
2814         }
2815         else if( !libname.IsFileReadable() )
2816         {
2817             ostringstream ostr;
2818             ostr << "\n* cannot read library file: '" << lfname << "'";
2819 
2820             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2821         }
2822         else
2823         {
2824             // read the library file before proceeding
2825             readLibFile( lfname );
2826         }
2827 
2828         // read the board file
2829         readBoardFile( bfname, aNoSubstituteOutlines );
2830     }
2831     catch( const std::exception& e )
2832     {
2833         Clear();
2834         errormsg = e.what();
2835 
2836         return false;
2837     }
2838 
2839     return true;
2840 }
2841 
2842 
writeLibFile(const std::string & aFileName)2843 bool IDF3_BOARD::writeLibFile( const std::string& aFileName )
2844 {
2845     OPEN_OSTREAM( lib, aFileName.c_str() );
2846 
2847     try
2848     {
2849         lib.exceptions( std::ios_base::failbit );
2850 
2851         if( lib.fail() )
2852         {
2853             ostringstream ostr;
2854             ostr << "\n* could not open file: '" << aFileName << "'";
2855 
2856             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2857         }
2858 
2859         lib.imbue( std::locale( "C" ) );
2860         wxDateTime tdate( time( nullptr ) );
2861 
2862         if( idfSource.empty() )
2863             idfSource = "KiCad-IDF Framework";
2864 
2865         ostringstream fileDate;
2866         fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
2867         fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
2868         fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
2869         libDate = fileDate.str();
2870 
2871         lib << ".HEADER\n";
2872         lib << "LIBRARY_FILE 3.0 \"Created by " << idfSource;
2873         lib << "\" " << libDate << " " << (++libFileVersion) << "\n";
2874         lib << ".END_HEADER\n\n";
2875 
2876         std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
2877         std::map< std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
2878 
2879         while( its != ite )
2880         {
2881             its->second->writeData( lib );
2882             ++its;
2883         }
2884 
2885     }
2886     catch( const std::exception& )
2887     {
2888         lib.exceptions( std::ios_base::goodbit );
2889         CLOSE_STREAM( lib );
2890         throw;
2891     }
2892 
2893     CLOSE_STREAM( lib );
2894     return true;
2895 }
2896 
2897 
writeBoardFile(const std::string & aFileName)2898 void IDF3_BOARD::writeBoardFile( const std::string& aFileName )
2899 {
2900     OPEN_OSTREAM( brd, aFileName.c_str() );
2901 
2902     try
2903     {
2904         brd.exceptions( std::ostream::failbit );
2905 
2906         if( brd.fail() )
2907         {
2908             ostringstream ostr;
2909             ostr << "\n* could not open file: '" << aFileName << "'";
2910 
2911             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2912         }
2913 
2914         brd.imbue( std::locale( "C" ) );
2915         wxDateTime tdate( time( nullptr ) );
2916 
2917         if( idfSource.empty() )
2918             idfSource = "KiCad-IDF Framework";
2919 
2920         ostringstream fileDate;
2921         fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
2922         fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
2923         fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
2924         brdDate = fileDate.str();
2925 
2926         brd << ".HEADER\n";
2927         brd << "BOARD_FILE 3.0 \"Created by " << idfSource;
2928         brd << "\" " << brdDate << " " << (++brdFileVersion) << "\n";
2929 
2930         if( boardName.empty() )
2931             brd << "\"BOARD WITH NO NAME\" ";
2932         else
2933             brd << "\"" << boardName << "\" ";
2934 
2935         brd << setw(1) << setfill( ' ' );
2936 
2937         if( unit == IDF3::UNIT_MM )
2938             brd << "MM\n";
2939         else
2940             brd << "THOU\n";
2941 
2942         brd << ".END_HEADER\n\n";
2943 
2944         // write the BOARD_OUTLINE
2945         olnBoard.writeData( brd );
2946 
2947         // OTHER outlines
2948         do
2949         {
2950             std::map<std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
2951             std::map<std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
2952 
2953             while(its != ite )
2954             {
2955                 its->second->writeData( brd );
2956                 ++its;
2957             }
2958 
2959         } while( 0 );
2960 
2961         // ROUTE outlines
2962         do
2963         {
2964             std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
2965             std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
2966 
2967             while( its != ite )
2968             {
2969                 (*its)->writeData( brd );
2970                 ++its;
2971             }
2972 
2973         } while( 0 );
2974 
2975         // PLACEMENT outlines
2976         do
2977         {
2978             std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
2979             std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
2980 
2981             while( its != ite )
2982             {
2983                 (*its)->writeData( brd );
2984                 ++its;
2985             }
2986 
2987         } while( 0 );
2988 
2989         // ROUTE KEEPOUT outlines
2990         do
2991         {
2992             std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
2993             std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
2994 
2995             while( its != ite )
2996             {
2997                 (*its)->writeData( brd );
2998                 ++its;
2999             }
3000 
3001         } while( 0 );
3002 
3003         // VIA KEEPOUT outlines
3004         do
3005         {
3006             std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
3007             std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
3008 
3009             while( its != ite )
3010             {
3011                 (*its)->writeData( brd );
3012                 ++its;
3013             }
3014 
3015         } while( 0 );
3016 
3017         // PLACE KEEPOUT outlines
3018         do
3019         {
3020             std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
3021             std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
3022 
3023             while( its != ite )
3024             {
3025                 (*its)->writeData( brd );
3026                 ++its;
3027             }
3028 
3029         } while( 0 );
3030 
3031         // PLACEMENT GROUP outlines
3032         do
3033         {
3034             std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
3035             std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
3036 
3037             while( its != ite )
3038             {
3039                 its->second->writeData( brd );
3040                 ++its;
3041             }
3042 
3043         } while( 0 );
3044 
3045         // Drilled holes
3046         do
3047         {
3048             std::list<std::string>::iterator itds = drillComments.begin();
3049             std::list<std::string>::iterator itde = drillComments.end();
3050 
3051             while( itds != itde )
3052             {
3053                 brd << "# " << *itds << "\n";
3054                 ++itds;
3055             }
3056 
3057             brd << ".DRILLED_HOLES\n";
3058 
3059             std::list<IDF_DRILL_DATA*>::iterator itbs = board_drills.begin();
3060             std::list<IDF_DRILL_DATA*>::iterator itbe = board_drills.end();
3061 
3062             while( itbs != itbe )
3063             {
3064                 (*itbs)->write( brd, unit );
3065                 ++itbs;
3066             }
3067 
3068             std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
3069             std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
3070 
3071             while( itcs != itce )
3072             {
3073                 itcs->second->writeDrillData( brd );
3074                 ++itcs;
3075             }
3076 
3077             brd << ".END_DRILLED_HOLES\n\n";
3078         } while( 0 );
3079 
3080         // Notes
3081         if( !notes.empty() )
3082         {
3083             std::list<std::string>::iterator itncs = noteComments.begin();
3084             std::list<std::string>::iterator itnce = noteComments.end();
3085 
3086             while( itncs != itnce )
3087             {
3088                 brd << "# " << *itncs << "\n";
3089                 ++itncs;
3090             }
3091 
3092             brd << ".NOTES\n";
3093 
3094             std::list<IDF_NOTE*>::iterator itns = notes.begin();
3095             std::list<IDF_NOTE*>::iterator itne = notes.end();
3096 
3097             while( itns != itne )
3098             {
3099                 (*itns)->writeNote( brd, unit );
3100                 ++itns;
3101             }
3102 
3103             brd << ".END_NOTES\n\n";
3104 
3105         }
3106 
3107         // Placement
3108         if( !components.empty() )
3109         {
3110             std::list<std::string>::iterator itpcs = placeComments.begin();
3111             std::list<std::string>::iterator itpce = placeComments.end();
3112 
3113             while( itpcs != itpce )
3114             {
3115                 brd << "# " << *itpcs << "\n";
3116                 ++itpcs;
3117             }
3118 
3119             std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
3120             std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
3121 
3122             // determine if there are any component outlines at all and avoid
3123             // writing an empty PLACEMENT section if there are no outlines.
3124             // this will cost a little time but prevents software such as
3125             // CircuitWorks from segfaulting on an empty section.
3126 
3127             bool hasOutlines = false;
3128 
3129             while( itcs != itce )
3130             {
3131                 if( itcs->second->GetOutlinesSize() > 0 )
3132                 {
3133                     itcs = components.begin();
3134                     hasOutlines = true;
3135                     break;
3136                 }
3137 
3138                 ++itcs;
3139             }
3140 
3141             if( hasOutlines )
3142             {
3143                 brd << ".PLACEMENT\n";
3144 
3145                 while( itcs != itce )
3146                 {
3147                     itcs->second->writePlaceData( brd );
3148                     ++itcs;
3149                 }
3150 
3151                 brd << ".END_PLACEMENT\n";
3152             }
3153 
3154         }
3155 
3156     }
3157     catch( const std::exception& )
3158     {
3159         brd.exceptions( std::ios_base::goodbit );
3160         CLOSE_STREAM( brd );
3161         throw;
3162     }
3163 
3164     CLOSE_STREAM( brd );
3165     return;
3166 }
3167 
3168 
WriteFile(const wxString & aFullFileName,bool aUnitMM,bool aForceUnitFlag)3169 bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aForceUnitFlag )
3170 {
3171     if( aUnitMM != IDF3::UNIT_THOU )
3172         setUnit( IDF3::UNIT_MM, aForceUnitFlag );
3173     else
3174         setUnit( IDF3::UNIT_THOU, aForceUnitFlag );
3175 
3176     // 1. Check that the file extension is 'emn'
3177     // 2. Write the *.emn file according to the IDFv3 spec
3178     // 3. Write the *.emp file according to the IDFv3 spec
3179 
3180     wxFileName brdname( aFullFileName );
3181     wxFileName libname( aFullFileName );
3182 
3183     brdname.SetExt( wxT( "emn" ) );
3184     libname.SetExt( wxT( "emp" ) );
3185 
3186     std::string bfname = TO_UTF8( aFullFileName );
3187 
3188     try
3189     {
3190         if( !brdname.IsOk() )
3191         {
3192             ostringstream ostr;
3193             ostr << "\n* invalid file name: '" << bfname << "'";
3194 
3195             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3196         }
3197 
3198         if( brdname.FileExists() && !brdname.IsFileWritable() )
3199         {
3200             ostringstream ostr;
3201             ostr << "cannot overwrite existing board file\n";
3202             ostr << "* filename: '" << bfname << "'";
3203 
3204             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3205         }
3206 
3207         bfname = TO_UTF8( brdname.GetFullPath() );
3208         std::string lfname = TO_UTF8( libname.GetFullPath() );
3209 
3210         if( libname.FileExists() && !libname.IsFileWritable() )
3211         {
3212             ostringstream ostr;
3213             ostr << "cannot overwrite existing library file\n";
3214             ostr << "* filename: '" << lfname << "'";
3215 
3216             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3217         }
3218 
3219         writeLibFile( lfname );
3220         writeBoardFile( bfname );
3221 
3222     }
3223     catch( const std::exception& e )
3224     {
3225         errormsg = e.what();
3226 
3227         return false;
3228     }
3229 
3230     return true;
3231 }
3232 
3233 
GetIDFSource(void)3234 const std::string& IDF3_BOARD::GetIDFSource( void )
3235 {
3236     return idfSource;
3237 }
3238 
3239 
SetIDFSource(const std::string & aIDFSource)3240 void IDF3_BOARD::SetIDFSource( const std::string& aIDFSource )
3241 {
3242     idfSource = aIDFSource;
3243     return;
3244 }
3245 
3246 
GetBoardSource(void)3247 const std::string& IDF3_BOARD::GetBoardSource( void )
3248 {
3249     return brdSource;
3250 }
3251 
3252 
GetLibrarySource(void)3253 const std::string& IDF3_BOARD::GetLibrarySource( void )
3254 {
3255     return libSource;
3256 }
3257 
3258 
GetBoardDate(void)3259 const std::string& IDF3_BOARD::GetBoardDate( void )
3260 {
3261     return brdDate;
3262 }
3263 
3264 
GetLibraryDate(void)3265 const std::string& IDF3_BOARD::GetLibraryDate( void )
3266 {
3267     return libDate;
3268 }
3269 
3270 
GetBoardVersion(void)3271 int IDF3_BOARD::GetBoardVersion( void )
3272 {
3273     return brdFileVersion;
3274 }
3275 
3276 
SetBoardVersion(int aVersion)3277 bool IDF3_BOARD::SetBoardVersion( int aVersion )
3278 {
3279     if( aVersion < 0 )
3280     {
3281         ostringstream ostr;
3282         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3283         ostr << "*  board version (" << aVersion << ") must be >= 0";
3284         errormsg = ostr.str();
3285 
3286         return false;
3287     }
3288 
3289     brdFileVersion = aVersion;
3290 
3291     return true;
3292 }
3293 
3294 
GetLibraryVersion(void)3295 int IDF3_BOARD::GetLibraryVersion( void )
3296 {
3297     return libFileVersion;
3298 }
3299 
3300 
SetLibraryVersion(int aVersion)3301 bool IDF3_BOARD::SetLibraryVersion( int aVersion )
3302 {
3303     if( aVersion < 0 )
3304     {
3305         ostringstream ostr;
3306         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3307         ostr << "* library version (" << aVersion << ") must be >= 0";
3308         errormsg = ostr.str();
3309 
3310         return false;
3311     }
3312 
3313     libFileVersion = aVersion;
3314 
3315     return true;
3316 }
3317 
3318 
GetUserScale(void)3319 double IDF3_BOARD::GetUserScale( void )
3320 {
3321     return userScale;
3322 }
3323 
3324 
SetUserScale(double aScaleFactor)3325 bool IDF3_BOARD::SetUserScale( double aScaleFactor )
3326 {
3327     if( aScaleFactor == 0.0 )
3328     {
3329         ostringstream ostr;
3330         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3331         ostr << "* BUG: user scale factor must not be 0";
3332         errormsg = ostr.str();
3333 
3334         return false;
3335     }
3336 
3337     userScale = aScaleFactor;
3338     return true;
3339 }
3340 
3341 
GetUserPrecision(void)3342 int IDF3_BOARD::GetUserPrecision( void )
3343 {
3344     return userPrec;
3345 }
3346 
3347 
SetUserPrecision(int aPrecision)3348 bool IDF3_BOARD::SetUserPrecision( int aPrecision )
3349 {
3350     if( aPrecision < 1 || aPrecision > 8 )
3351     {
3352         ostringstream ostr;
3353         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3354         ostr << "* precision value (" << aPrecision << ") must be 1..8";
3355         errormsg = ostr.str();
3356 
3357         return false;
3358     }
3359 
3360     userPrec = aPrecision;
3361     return true;
3362 }
3363 
3364 
GetUserOffset(double & aXoff,double & aYoff)3365 void IDF3_BOARD::GetUserOffset( double& aXoff, double& aYoff )
3366 {
3367     aXoff = userXoff;
3368     aYoff = userYoff;
3369 }
3370 
3371 
SetUserOffset(double aXoff,double aYoff)3372 void IDF3_BOARD::SetUserOffset( double aXoff, double aYoff )
3373 {
3374     userXoff = aXoff;
3375     userYoff = aYoff;
3376 }
3377 
3378 
AddBoardOutline(IDF_OUTLINE * aOutline)3379 bool IDF3_BOARD::AddBoardOutline( IDF_OUTLINE* aOutline )
3380 {
3381     if( !olnBoard.AddOutline( aOutline ) )
3382     {
3383         errormsg = olnBoard.GetError();
3384 
3385         return false;
3386     }
3387 
3388     return true;
3389 }
3390 
3391 
DelBoardOutline(IDF_OUTLINE * aOutline)3392 bool IDF3_BOARD::DelBoardOutline( IDF_OUTLINE* aOutline )
3393 {
3394     if( !olnBoard.DelOutline( aOutline ) )
3395     {
3396         errormsg = olnBoard.GetError();
3397         return false;
3398     }
3399 
3400     return true;
3401 }
3402 
3403 
DelBoardOutline(size_t aIndex)3404 bool IDF3_BOARD::DelBoardOutline( size_t aIndex )
3405 {
3406     if( !olnBoard.DelOutline( aIndex ) )
3407     {
3408         errormsg = olnBoard.GetError();
3409         return false;
3410     }
3411 
3412     return true;
3413 }
3414 
3415 
GetBoardOutlinesSize(void)3416 size_t IDF3_BOARD::GetBoardOutlinesSize( void )
3417 {
3418     return olnBoard.OutlinesSize();
3419 }
3420 
3421 
GetBoardOutline(void)3422 BOARD_OUTLINE* IDF3_BOARD::GetBoardOutline( void )
3423 {
3424     return &olnBoard;
3425 }
3426 
3427 
GetBoardOutlines(void)3428 const std::list< IDF_OUTLINE* >*const IDF3_BOARD::GetBoardOutlines( void )
3429 {
3430     return olnBoard.GetOutlines();
3431 }
3432 
3433 
AddBoardDrill(double aDia,double aXpos,double aYpos,IDF3::KEY_PLATING aPlating,const std::string & aHoleType,IDF3::KEY_OWNER aOwner)3434 IDF_DRILL_DATA* IDF3_BOARD::AddBoardDrill( double aDia, double aXpos, double aYpos,
3435                                            IDF3::KEY_PLATING aPlating,
3436                                            const std::string& aHoleType,
3437                                            IDF3::KEY_OWNER aOwner )
3438 {
3439     IDF_DRILL_DATA* drill = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
3440                                                 "BOARD", aHoleType, aOwner );
3441 
3442     if( drill != nullptr )
3443         board_drills.push_back( drill );
3444 
3445     return drill;
3446 }
3447 
3448 
AddDrill(IDF_DRILL_DATA * aDrilledHole)3449 IDF_DRILL_DATA* IDF3_BOARD::AddDrill( IDF_DRILL_DATA* aDrilledHole )
3450 {
3451     if( !aDrilledHole )
3452         return nullptr;
3453 
3454     // note: PANEL drills are essentially BOARD drills which
3455     // the panel requires to be present
3456     if( CompareToken( "BOARD", aDrilledHole->GetDrillRefDes() )
3457         || CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
3458     {
3459         board_drills.push_back( aDrilledHole );
3460         return aDrilledHole;
3461     }
3462 
3463     return addCompDrill( aDrilledHole );
3464 }
3465 
3466 
DelBoardDrill(double aDia,double aXpos,double aYpos)3467 bool IDF3_BOARD::DelBoardDrill( double aDia, double aXpos, double aYpos )
3468 {
3469     errormsg.clear();
3470 
3471     std::list<IDF_DRILL_DATA*>::iterator sp = board_drills.begin();
3472     std::list<IDF_DRILL_DATA*>::iterator ep = board_drills.end();
3473     bool rval = false;
3474 
3475     while( sp != ep )
3476     {
3477         if( (*sp)->Matches( aDia, aXpos, aYpos ) )
3478         {
3479 #ifndef DISABLE_IDF_OWNERSHIP
3480             IDF3::KEY_OWNER keyo = (*sp)->GetDrillOwner();
3481 
3482             if( keyo == UNOWNED || ( keyo == MCAD && cadType == CAD_MECH )
3483                 || ( keyo == ECAD && cadType == CAD_ELEC ) )
3484             {
3485                 rval = true;
3486                 delete *sp;
3487                 sp = board_drills.erase( sp );
3488                 continue;
3489             }
3490             else
3491             {
3492                 ostringstream ostr;
3493                 ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3494                 ostr << "* ownership violation; drill owner (";
3495 
3496                 switch( keyo )
3497                 {
3498                 case ECAD:
3499                     ostr << "ECAD";
3500                     break;
3501 
3502                 case MCAD:
3503                     ostr << "MCAD";
3504                     break;
3505 
3506                 default:
3507                     ostr << "invalid: " << keyo;
3508                     break;
3509                 }
3510 
3511                 ostr << ") may not be modified by ";
3512 
3513                 if( cadType == CAD_MECH )
3514                     ostr << "MCAD";
3515                 else
3516                     ostr << "ECAD";
3517 
3518                 errormsg = ostr.str();
3519 
3520                 ++sp;
3521                 continue;
3522             }
3523 #else
3524             rval = true;
3525             delete *sp;
3526             sp = board_drills.erase( sp );
3527             continue;
3528 #endif
3529         }
3530 
3531         ++sp;
3532     }
3533 
3534     return rval;
3535 }
3536 
3537 
AddSlot(double aWidth,double aLength,double aOrientation,double aX,double aY)3538 bool IDF3_BOARD::AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY )
3539 {
3540     if( aWidth < IDF_MIN_DIA_MM )
3541     {
3542         ostringstream ostr;
3543         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3544         ostr << "* slot width (" << aWidth << ") must be >= " << IDF_MIN_DIA_MM;
3545         errormsg = ostr.str();
3546 
3547         return false;
3548     }
3549 
3550     if( aLength < IDF_MIN_DIA_MM )
3551     {
3552         ostringstream ostr;
3553         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3554         ostr << "* slot length (" << aLength << ") must be >= " << IDF_MIN_DIA_MM;
3555         errormsg = ostr.str();
3556 
3557         return false;
3558     }
3559 
3560     IDF_POINT c[2];     // centers
3561     IDF_POINT pt[4];
3562 
3563     double a1 = aOrientation / 180.0 * M_PI;
3564     double a2 = a1 + M_PI_2;
3565     double d1 = aLength / 2.0;
3566     double d2 = aWidth / 2.0;
3567     double sa1 = sin( a1 );
3568     double ca1 = cos( a1 );
3569     double dsa2 = d2 * sin( a2 );
3570     double dca2 = d2 * cos( a2 );
3571 
3572     c[0].x  = aX + d1 * ca1;
3573     c[0].y  = aY + d1 * sa1;
3574 
3575     c[1].x  = aX - d1 * ca1;
3576     c[1].y  = aY - d1 * sa1;
3577 
3578     pt[0].x = c[0].x - dca2;
3579     pt[0].y = c[0].y - dsa2;
3580 
3581     pt[1].x = c[1].x - dca2;
3582     pt[1].y = c[1].y - dsa2;
3583 
3584     pt[2].x = c[1].x + dca2;
3585     pt[2].y = c[1].y + dsa2;
3586 
3587     pt[3].x = c[0].x + dca2;
3588     pt[3].y = c[0].y + dsa2;
3589 
3590     IDF_OUTLINE* outline = new IDF_OUTLINE;
3591 
3592     if( outline == nullptr )
3593     {
3594         ostringstream ostr;
3595         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3596         ostr << "* could not create an outline object";
3597         errormsg = ostr.str();
3598 
3599         return false;
3600     }
3601 
3602     // first straight run
3603     IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] );
3604     outline->push( seg );
3605 
3606     // first 180 degree cap
3607     seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true );
3608     outline->push( seg );
3609 
3610     // final straight run
3611     seg = new IDF_SEGMENT( pt[2], pt[3] );
3612     outline->push( seg );
3613 
3614     // final 180 degree cap
3615     seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true );
3616     outline->push( seg );
3617 
3618     if( !olnBoard.addOutline( outline ) )
3619     {
3620         errormsg = olnBoard.GetError();
3621         return false;
3622     }
3623 
3624     return true;
3625 }
3626 
3627 
addCompDrill(double aDia,double aXpos,double aYpos,IDF3::KEY_PLATING aPlating,const std::string & aHoleType,IDF3::KEY_OWNER aOwner,const std::string & aRefDes)3628 IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( double aDia, double aXpos, double aYpos,
3629                                           IDF3::KEY_PLATING aPlating,
3630                                           const std::string& aHoleType,
3631                                           IDF3::KEY_OWNER aOwner,
3632                                           const std::string& aRefDes )
3633 {
3634     // first find the matching component; if it doesn't exist we must create it somehow -
3635     // question is, do we need a component outline at this stage or can those be added later?
3636     //
3637     // Presumably we can create a component with no outline and add the outlines later.
3638     // If a component is created and an outline specified but the outline is not loaded,
3639     // we're screwed if (a) we have already read the library file (*.emp) or (b) we don't
3640     // know the filename
3641 
3642     std::string refdes = aRefDes;
3643 
3644     // note: for components 'NOREFDES' would be assigned a Unique ID, but for holes
3645     // there is no way of associating the hole with the correct entity (if any)
3646     // so a hole added with "NOREFDES" goes to a generic component "NOREFDES"
3647     if( refdes.empty() )
3648         refdes = "NOREFDES";
3649 
3650     // check if the target is BOARD or PANEL
3651     if( CompareToken( "BOARD", refdes ) )
3652         return AddBoardDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
3653 
3654     if( CompareToken( "PANEL", refdes ) )
3655     {
3656         ostringstream ostr;
3657         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3658         ostr << "* PANEL data not supported";
3659         errormsg = ostr.str();
3660 
3661         return nullptr;
3662     }
3663 
3664     std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( refdes );
3665 
3666     if( ref == components.end() )
3667     {
3668         // create the item
3669         IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
3670 
3671         if( comp == nullptr )
3672         {
3673             ostringstream ostr;
3674             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3675             ostr << "* could not create new component object";
3676             errormsg = ostr.str();
3677 
3678             return nullptr;
3679         }
3680 
3681         comp->SetParent( this );
3682         comp->SetRefDes( refdes );
3683         ref = components.insert( std::pair< std::string,
3684                                  IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
3685     }
3686 
3687     // add the drill
3688     IDF_DRILL_DATA* dp = ref->second->AddDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
3689 
3690     if( !dp )
3691     {
3692         errormsg = ref->second->GetError();
3693         return nullptr;
3694     }
3695 
3696     return dp;
3697 }
3698 
3699 
addCompDrill(IDF_DRILL_DATA * aDrilledHole)3700 IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( IDF_DRILL_DATA* aDrilledHole )
3701 {
3702     if( !aDrilledHole )
3703     {
3704         ostringstream ostr;
3705         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): nullptr pointer";
3706         errormsg = ostr.str();
3707 
3708         return nullptr;
3709     }
3710 
3711     if( CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
3712     {
3713         ostringstream ostr;
3714         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3715         ostr << "* PANEL data not supported";
3716         errormsg = ostr.str();
3717 
3718         return nullptr;
3719     }
3720 
3721     std::map<std::string, IDF3_COMPONENT*>::iterator ref =
3722             components.find( aDrilledHole->GetDrillRefDes() );
3723 
3724     if( ref == components.end() )
3725     {
3726         // create the item
3727         IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
3728 
3729         if( comp == nullptr )
3730         {
3731             ostringstream ostr;
3732             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3733             ostr << "* could not create new component object";
3734             errormsg = ostr.str();
3735 
3736             return nullptr;
3737         }
3738 
3739         comp->SetParent( this );
3740         comp->SetRefDes( aDrilledHole->GetDrillRefDes() );
3741         ref = components.insert( std::pair< std::string,
3742                                  IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
3743     }
3744 
3745     IDF_DRILL_DATA* dp = ref->second->AddDrill( aDrilledHole );
3746 
3747     if( !dp )
3748     {
3749         errormsg = ref->second->GetError();
3750         return nullptr;
3751     }
3752 
3753     return dp;
3754 }
3755 
3756 
delCompDrill(double aDia,double aXpos,double aYpos,const std::string & aRefDes)3757 bool IDF3_BOARD::delCompDrill( double aDia, double aXpos, double aYpos, const std::string& aRefDes )
3758 {
3759     errormsg.clear();
3760 
3761     std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( aRefDes );
3762 
3763     if( ref == components.end() )
3764         return false;
3765 
3766     if( !ref->second->DelDrill( aDia, aXpos, aYpos ) )
3767     {
3768         errormsg = ref->second->GetError();
3769         return false;
3770     }
3771 
3772     return true;
3773 }
3774 
3775 
AddComponent(IDF3_COMPONENT * aComponent)3776 bool IDF3_BOARD::AddComponent( IDF3_COMPONENT* aComponent )
3777 {
3778     if( !aComponent )
3779     {
3780         ostringstream ostr;
3781         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__;
3782         ostr << "(): Invalid component pointer (nullptr)";
3783         errormsg = ostr.str();
3784 
3785         return false;
3786     }
3787 
3788     if( components.insert( std::pair<std::string, IDF3_COMPONENT*>
3789         ( aComponent->GetRefDes(), aComponent ) ).second == false )
3790     {
3791         ostringstream ostr;
3792         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
3793         ostr << "* duplicate RefDes ('" << aComponent->GetRefDes() << "')";
3794         errormsg = ostr.str();
3795 
3796         return false;
3797     }
3798 
3799     return true;
3800 }
3801 
3802 
DelComponent(IDF3_COMPONENT * aComponent)3803 bool IDF3_BOARD::DelComponent( IDF3_COMPONENT* aComponent )
3804 {
3805     errormsg.clear();
3806 
3807 #ifndef DISABLE_IDF_OWNERSHIP
3808     if( !checkComponentOwnership( __LINE__, __FUNCTION__, aComponent ) )
3809         return false;
3810 #endif
3811 
3812     std::map<std::string, IDF3_COMPONENT*>::iterator it =
3813         components.find( aComponent->GetRefDes() );
3814 
3815     if( it == components.end() )
3816         return false;
3817 
3818     delete it->second;
3819     components.erase( it );
3820 
3821     return true;
3822 }
3823 
3824 
DelComponent(size_t aIndex)3825 bool IDF3_BOARD::DelComponent( size_t aIndex )
3826 {
3827     if( aIndex >= components.size() )
3828     {
3829         ostringstream ostr;
3830         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
3831         ostr << "* aIndex (" << aIndex << ") out of range (" << components.size() << ")";
3832         errormsg = ostr.str();
3833 
3834         return false;
3835     }
3836 
3837     std::map<std::string, IDF3_COMPONENT*>::iterator it = components.begin();
3838 
3839     while( aIndex-- > 0 ) ++it;
3840 
3841 #ifndef DISABLE_IDF_OWNERSHIP
3842     if( !checkComponentOwnership( __LINE__, __FUNCTION__, it->second ) )
3843         return false;
3844 #endif
3845 
3846     delete it->second;
3847     components.erase( it );
3848 
3849     return true;
3850 }
3851 
3852 
GetComponentsSize(void)3853 size_t IDF3_BOARD::GetComponentsSize( void )
3854 {
3855     return components.size();
3856 }
3857 
3858 
GetComponents(void)3859 std::map< std::string, IDF3_COMPONENT* >*const IDF3_BOARD::GetComponents( void )
3860 {
3861     return &components;
3862 }
3863 
3864 
FindComponent(const std::string & aRefDes)3865 IDF3_COMPONENT* IDF3_BOARD::FindComponent( const std::string& aRefDes )
3866 {
3867     std::map<std::string, IDF3_COMPONENT*>::iterator it = components.find( aRefDes );
3868 
3869     if( it == components.end() )
3870         return nullptr;
3871 
3872     return it->second;
3873 }
3874 
3875 
GetComponentOutline(const wxString & aFullFileName)3876 IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const wxString& aFullFileName )
3877 {
3878     std::string fname = TO_UTF8( aFullFileName );
3879     wxFileName idflib( aFullFileName );
3880 
3881     if( !idflib.IsOk() )
3882     {
3883         ostringstream ostr;
3884         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
3885         cerr << "* invalid file name: '" << fname << "'";
3886         errormsg = ostr.str();
3887 
3888         return nullptr;
3889     }
3890 
3891     if( !idflib.FileExists() )
3892     {
3893         ostringstream ostr;
3894         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
3895         cerr << "* no such file: '" << fname  << "'";
3896         errormsg = ostr.str();
3897 
3898         return nullptr;
3899     }
3900 
3901     if( !idflib.IsFileReadable() )
3902     {
3903         ostringstream ostr;
3904         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
3905         cerr << "* cannot read file: '" << fname << "'";
3906         errormsg = ostr.str();
3907 
3908         return nullptr;
3909     }
3910 
3911     std::map< std::string, std::string >::iterator itm = uidFileList.find( fname );
3912 
3913     if( itm != uidFileList.end() )
3914         return GetComponentOutline( itm->second );
3915 
3916     IDF3_COMP_OUTLINE* cp = new IDF3_COMP_OUTLINE( this );
3917 
3918     if( cp == nullptr )
3919     {
3920         ostringstream ostr;
3921         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
3922         cerr << "* failed to create outline\n";
3923         cerr << "* filename: '" << fname << "'";
3924         errormsg = ostr.str();
3925 
3926         return nullptr;
3927     }
3928 
3929     OPEN_ISTREAM( model, fname.c_str() );
3930 
3931     try
3932     {
3933         model.exceptions ( std::ios_base::badbit );
3934 
3935         if( model.fail() )
3936         {
3937             ostringstream ostr;
3938             ostr << "\n* could not open file: '" << fname << "'";
3939 
3940             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3941         }
3942 
3943         model.imbue( std::locale( "C" ) );
3944         std::string iline;      // the input line
3945         bool isComment;         // true if a line just read in is a comment line
3946         std::streampos pos;
3947 
3948         while( true )
3949         {
3950             while( !FetchIDFLine( model, iline, isComment, pos ) && model.good() );
3951 
3952             if( !model.good() )
3953             {
3954                 ostringstream ostr;
3955                 ostr << "\n* problems reading file: '" << fname << "'";
3956                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3957             }
3958 
3959             // accept comment lines, .ELECTRICAL, or .MECHANICAL only
3960             if( isComment )
3961             {
3962                 cp->AddComment( iline );
3963                 continue;
3964             }
3965 
3966             if( CompareToken( ".ELECTRICAL", iline ) || CompareToken( ".MECHANICAL", iline ) )
3967             {
3968                 cp->readData( model, iline, idfVer );
3969                 break;
3970             }
3971             else
3972             {
3973                 ostringstream ostr;
3974                 ostr << "faulty IDF component definition\n";
3975                 ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << iline << "'\n";
3976                 cerr << "* File: '" << fname << "'\n";
3977 
3978                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3979             }
3980         }
3981     }
3982     catch( const std::exception& e )
3983     {
3984         delete cp;
3985         model.exceptions ( std::ios_base::goodbit );
3986         CLOSE_STREAM( model );
3987         errormsg = e.what();
3988         return nullptr;
3989     }
3990 
3991     CLOSE_STREAM( model );
3992 
3993     // check the unique ID against the list from library components
3994     std::list< std::string >::iterator lsts = uidLibList.begin();
3995     std::list< std::string >::iterator lste = uidLibList.end();
3996     std::string uid = cp->GetUID();
3997     IDF3_COMP_OUTLINE* oldp = nullptr;
3998 
3999     while( lsts != lste )
4000     {
4001         if( ! lsts->compare( uid ) )
4002         {
4003             oldp = GetComponentOutline( uid );
4004 
4005             if( MatchCompOutline( cp, oldp ) )
4006             {
4007                 // everything is fine; the outlines are genuine duplicates; delete the copy
4008                 delete cp;
4009 
4010                 // make sure we can find the item via its filename
4011                 uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
4012 
4013                 // return the pointer to the original
4014                 return oldp;
4015             }
4016             else
4017             {
4018                 delete cp;
4019                 ostringstream ostr;
4020                 ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
4021                 ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
4022                 ostr << "* original loaded from library, duplicate in current file\n";
4023                 ostr << "* file: '" << fname << "'";
4024 
4025                 errormsg = ostr.str();
4026                 return nullptr;
4027             }
4028         }
4029 
4030         ++lsts;
4031     }
4032 
4033     // if we got this far then any duplicates are from files previously read
4034     oldp = GetComponentOutline( uid );
4035 
4036     if( oldp == nullptr )
4037     {
4038         // everything is fine, there are no existing entries
4039         uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
4040         compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( uid, cp ) );
4041 
4042         return cp;
4043     }
4044 
4045     if( MatchCompOutline( cp, oldp ) )
4046     {
4047         // everything is fine; the outlines are genuine duplicates; delete the copy
4048         delete cp;
4049 
4050         // make sure we can find the item via its other filename
4051         uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
4052 
4053         // return the pointer to the original
4054         return oldp;
4055     }
4056 
4057     delete cp;
4058 
4059     // determine the file name of the first instance
4060     std::map< std::string, std::string >::iterator ufls = uidFileList.begin();
4061     std::map< std::string, std::string >::iterator ufle = uidFileList.end();
4062     std::string oldfname;
4063 
4064     while( ufls != ufle )
4065     {
4066         if( ! ufls->second.compare( uid ) )
4067         {
4068             oldfname = ufls->first;
4069             break;
4070         }
4071 
4072         ++ufls;
4073     }
4074 
4075     ostringstream ostr;
4076     ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
4077     ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
4078     ostr << "* original file: '" << oldfname << "'\n";
4079     ostr << "* this file: '" << fname << "'";
4080 
4081     errormsg = ostr.str();
4082     return nullptr;
4083 }
4084 
4085 
GetComponentOutline(const std::string & aComponentID)4086 IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string& aComponentID )
4087 {
4088     std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.find( aComponentID );
4089 
4090     if( its != compOutlines.end() )
4091         return its->second;
4092 
4093     return nullptr;
4094 }
4095 
4096 
GetInvalidOutline(const std::string & aGeomName,const std::string & aPartName)4097 IDF3_COMP_OUTLINE* IDF3_BOARD::GetInvalidOutline( const std::string& aGeomName,
4098                                                   const std::string& aPartName )
4099 {
4100     std::string uid;
4101     bool empty = false;
4102 
4103     if( aGeomName.empty() && aPartName.empty() )
4104     {
4105         uid = "NOGEOM_NOPART";
4106         empty = true;
4107     }
4108     else
4109     {
4110         uid = aGeomName + "_" + aPartName;
4111     }
4112 
4113     IDF3_COMP_OUTLINE* cp = GetComponentOutline( uid );
4114 
4115     if( cp != nullptr )
4116         return cp;
4117 
4118     cp = new IDF3_COMP_OUTLINE( this );
4119 
4120     if( cp == nullptr )
4121     {
4122         ostringstream ostr;
4123         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
4124         cerr << "could not create new outline";
4125         errormsg = ostr.str();
4126 
4127         return nullptr;
4128     }
4129 
4130     if( empty )
4131         cp->CreateDefaultOutline( "", "" );
4132     else
4133         cp->CreateDefaultOutline( aGeomName, aPartName );
4134 
4135     compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>(cp->GetUID(), cp) );
4136 
4137     return cp;
4138 }
4139 
4140 
Clear(void)4141 void IDF3_BOARD::Clear( void )
4142 {
4143     // preserve the board thickness
4144     double thickness = olnBoard.GetThickness();
4145 
4146     idfSource.clear();
4147     brdSource.clear();
4148     libSource.clear();
4149     brdDate.clear();
4150     libDate.clear();
4151     uidFileList.clear();
4152     uidLibList.clear();
4153     brdFileVersion = 0;
4154     libFileVersion = 0;
4155     iRefDes = 0;
4156     sRefDes.clear();
4157 
4158     // delete comment lists
4159     noteComments.clear();
4160     drillComments.clear();
4161     placeComments.clear();
4162 
4163     // delete notes
4164     while( !notes.empty() )
4165     {
4166         delete notes.front();
4167         notes.pop_front();
4168     }
4169 
4170     // delete drill list
4171     do
4172     {
4173         std::list<IDF_DRILL_DATA*>::iterator ds = board_drills.begin();
4174         std::list<IDF_DRILL_DATA*>::iterator de = board_drills.end();
4175 
4176         while( ds != de )
4177         {
4178             delete *ds;
4179             ++ds;
4180         }
4181 
4182         board_drills.clear();
4183     } while( 0 );
4184 
4185 
4186     // delete components
4187     do
4188     {
4189         std::map<std::string, IDF3_COMPONENT*>::iterator cs = components.begin();
4190         std::map<std::string, IDF3_COMPONENT*>::iterator ce = components.end();
4191 
4192         while( cs != ce )
4193         {
4194             delete cs->second;
4195             ++cs;
4196         }
4197 
4198         components.clear();
4199     } while( 0 );
4200 
4201 
4202     // delete component outlines
4203     do
4204     {
4205         std::map<std::string, IDF3_COMP_OUTLINE*>::iterator cs = compOutlines.begin();
4206         std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ce = compOutlines.end();
4207 
4208         while( cs != ce )
4209         {
4210             delete cs->second;
4211             ++cs;
4212         }
4213 
4214         compOutlines.clear();
4215     } while( 0 );
4216 
4217 
4218     // delete OTHER outlines
4219     do
4220     {
4221         std::map<std::string, OTHER_OUTLINE*>::iterator os = olnOther.begin();
4222         std::map<std::string, OTHER_OUTLINE*>::iterator oe = olnOther.end();
4223 
4224         while( os != oe )
4225         {
4226             delete os->second;
4227             ++os;
4228         }
4229 
4230         olnOther.clear();
4231     } while( 0 );
4232 
4233 
4234     // delete ROUTE outlines
4235     do
4236     {
4237         std::list<ROUTE_OUTLINE*>::iterator os = olnRoute.begin();
4238         std::list<ROUTE_OUTLINE*>::iterator oe = olnRoute.end();
4239 
4240         while( os != oe )
4241         {
4242             delete *os;
4243             ++os;
4244         }
4245 
4246         olnRoute.clear();
4247     } while( 0 );
4248 
4249 
4250     // delete PLACE outlines
4251     do
4252     {
4253         std::list<PLACE_OUTLINE*>::iterator os = olnPlace.begin();
4254         std::list<PLACE_OUTLINE*>::iterator oe = olnPlace.end();
4255 
4256         while( os != oe )
4257         {
4258             delete *os;
4259             ++os;
4260         }
4261 
4262         olnPlace.clear();
4263     } while( 0 );
4264 
4265 
4266     // delete ROUTE KEEPOUT outlines
4267     do
4268     {
4269         std::list<ROUTE_KO_OUTLINE*>::iterator os = olnRouteKeepout.begin();
4270         std::list<ROUTE_KO_OUTLINE*>::iterator oe = olnRouteKeepout.end();
4271 
4272         while( os != oe )
4273         {
4274             delete *os;
4275             ++os;
4276         }
4277 
4278         olnRouteKeepout.clear();
4279     } while( 0 );
4280 
4281 
4282     // delete VIA KEEPOUT outlines
4283     do
4284     {
4285         std::list<VIA_KO_OUTLINE*>::iterator os = olnViaKeepout.begin();
4286         std::list<VIA_KO_OUTLINE*>::iterator oe = olnViaKeepout.end();
4287 
4288         while( os != oe )
4289         {
4290             delete *os;
4291             ++os;
4292         }
4293 
4294         olnViaKeepout.clear();
4295     } while( 0 );
4296 
4297 
4298     // delete PLACEMENT KEEPOUT outlines
4299     do
4300     {
4301         std::list<PLACE_KO_OUTLINE*>::iterator os = olnPlaceKeepout.begin();
4302         std::list<PLACE_KO_OUTLINE*>::iterator oe = olnPlaceKeepout.end();
4303 
4304         while( os != oe )
4305         {
4306             delete *os;
4307             ++os;
4308         }
4309 
4310         olnPlaceKeepout.clear();
4311     } while( 0 );
4312 
4313 
4314     // delete PLACEMENT GROUP outlines
4315     do
4316     {
4317         std::multimap<std::string, GROUP_OUTLINE*>::iterator os = olnGroup.begin();
4318         std::multimap<std::string, GROUP_OUTLINE*>::iterator oe = olnGroup.end();
4319 
4320         while( os != oe )
4321         {
4322             delete os->second;
4323             ++os;
4324         }
4325 
4326         olnGroup.clear();
4327     } while( 0 );
4328 
4329     boardName.clear();
4330     olnBoard.setThickness( thickness );
4331 
4332     unit      = UNIT_MM;
4333     userScale = 1.0;
4334     userXoff  = 0.0;
4335     userYoff  = 0.0;
4336 }
4337 
4338 
GetOtherOutlines(void)4339 const std::map<std::string, OTHER_OUTLINE*>* const IDF3_BOARD::GetOtherOutlines( void )
4340 {
4341     return &olnOther;
4342 }
4343