1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>.
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 #include <wx/ffile.h>
27 #include <wx/log.h>
28 #include <eda_item.h>
29 #include <locale_io.h>
30 #include <string_utils.h>
31 #include <drawing_sheet/ds_data_item.h>
32 #include <drawing_sheet/ds_data_model.h>
33 #include <drawing_sheet/ds_painter.h>
34 #include <drawing_sheet/drawing_sheet_lexer.h>
35 #include <drawing_sheet/ds_file_versions.h>
36 
37 using namespace DRAWINGSHEET_T;
38 
39 /**
40  * DRAWING_SHEET_PARSER
41  * holds data and functions pertinent to parsing a S-expression file
42  * for a DS_DATA_MODEL.
43  */
44 class DRAWING_SHEET_PARSER : public DRAWING_SHEET_LEXER
45 {
46 public:
47     DRAWING_SHEET_PARSER( const char* aLine, const wxString& aSource );
48     void Parse( DS_DATA_MODEL* aLayout );
49 
50 private:
51     int m_requiredVersion;
52 
53     /**
54      * Parse the data specified at the very beginning of the file, like version and the
55      * application used to create this file.
56      */
57     void parseHeader( T aHeaderType );
58 
59     /**
60      * Parse an integer.
61      */
62     int parseInt();
63 
64     /**
65      * Parse an integer and constrain it between two values.
66      *
67      * @param aMin is the smallest return value.
68      * @param aMax is the largest return value.
69      * @return int - the parsed integer.
70      */
71     int parseInt( int aMin, int aMax );
72 
73     /**
74      * Parse a double.
75      *
76      * @return double - the parsed double.
77      */
78     double parseDouble();
79 
80     void parseSetup( DS_DATA_MODEL* aLayout );
81 
82     /**
83      * Parse a graphic item starting by "(line" or "(rect" and read parameters.
84      */
85     void parseGraphic( DS_DATA_ITEM * aItem );
86 
87     /**
88      * Parse a text item starting by "(tbtext" and read parameters.
89      */
90     void parseText( DS_DATA_ITEM_TEXT * aItem );
91 
92     /**
93      * Parse a polygon item starting by "( polygon" and read parameters.
94      * the list of corners included in this description is read by parsePolyOutline.
95      */
96     void parsePolygon( DS_DATA_ITEM_POLYGONS * aItem );
97 
98     /**
99      * Parse a list of corners starting by "( pts" and read coordinates.
100      */
101     void parsePolyOutline( DS_DATA_ITEM_POLYGONS * aItem );
102 
103 
104     /**
105      * Parse a bitmap item starting by "( bitmap" and read parameters.
106      */
107     void parseBitmap( DS_DATA_ITEM_BITMAP * aItem );
108 
109     void parseCoordinate( POINT_COORD& aCoord);
110     void readOption( DS_DATA_ITEM * aItem );
111     void readPngdata( DS_DATA_ITEM_BITMAP * aItem );
112 };
113 
114 // PCB_PLOT_PARAMS_PARSER
115 
DRAWING_SHEET_PARSER(const char * aLine,const wxString & aSource)116 DRAWING_SHEET_PARSER::DRAWING_SHEET_PARSER( const char* aLine,
117                                             const wxString& aSource ) :
118         DRAWING_SHEET_LEXER( aLine, aSource ),
119         m_requiredVersion( 0 )
120 {
121 }
122 
123 
convertLegacyVariableRefs(const wxString & aTextbase)124 wxString convertLegacyVariableRefs( const wxString& aTextbase )
125 {
126     wxString msg;
127 
128     /*
129      * Legacy formats
130      * %% = replaced by %
131      * %K = Kicad version
132      * %Z = paper format name (A4, USLetter)
133      * %Y = company name
134      * %D = date
135      * %R = revision
136      * %S = sheet number
137      * %N = number of sheets
138      * %L = layer name
139      * %Cx = comment (x = 0 to 9 to identify the comment)
140      * %F = filename
141      * %P = sheet path (sheet full name)
142      * %T = title
143      */
144 
145     for( unsigned ii = 0; ii < aTextbase.Len(); ii++ )
146     {
147         if( aTextbase[ii] != '%' )
148         {
149             msg << aTextbase[ii];
150             continue;
151         }
152 
153         if( ++ii >= aTextbase.Len() )
154             break;
155 
156         wxChar format = aTextbase[ii];
157 
158         switch( format )
159         {
160             case '%': msg += '%';                       break;
161             case 'D': msg += wxT( "${ISSUE_DATE}" );    break;
162             case 'R': msg += wxT( "${REVISION}" );      break;
163             case 'K': msg += wxT( "${KICAD_VERSION}" ); break;
164             case 'Z': msg += wxT( "${PAPER}" );         break;
165             case 'S': msg += wxT( "${#}" );             break;
166             case 'N': msg += wxT( "${##}" );            break;
167             case 'F': msg += wxT( "${FILENAME}" );      break;
168             case 'L': msg += wxT( "${LAYER}" );         break;
169             case 'P': msg += wxT( "${SHEETNAME}" );     break;
170             case 'Y': msg += wxT( "${COMPANY}" );       break;
171             case 'T': msg += wxT( "${TITLE}" );         break;
172             case 'C':
173                 format = aTextbase[++ii];
174 
175                 switch( format )
176                 {
177                 case '0': msg += wxT( "${COMMENT1}" );  break;
178                 case '1': msg += wxT( "${COMMENT2}" );  break;
179                 case '2': msg += wxT( "${COMMENT3}" );  break;
180                 case '3': msg += wxT( "${COMMENT4}" );  break;
181                 case '4': msg += wxT( "${COMMENT5}" );  break;
182                 case '5': msg += wxT( "${COMMENT6}" );  break;
183                 case '6': msg += wxT( "${COMMENT7}" );  break;
184                 case '7': msg += wxT( "${COMMENT8}" );  break;
185                 case '8': msg += wxT( "${COMMENT9}" );  break;
186                 }
187                 break;
188 
189             default:
190                 break;
191         }
192     }
193 
194     return msg;
195 }
196 
197 
Parse(DS_DATA_MODEL * aLayout)198 void DRAWING_SHEET_PARSER::Parse( DS_DATA_MODEL* aLayout )
199 {
200     DS_DATA_ITEM* item;
201     LOCALE_IO     toggle;
202 
203     NeedLEFT();
204     T token = NextTok();
205 
206     parseHeader( token );
207     aLayout->SetFileFormatVersionAtLoad( m_requiredVersion );
208 
209     for( token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
210     {
211         if( token == T_LEFT )
212             token = NextTok();
213 
214         switch( token )
215         {
216         case T_setup:   // Defines default values for graphic items
217             parseSetup( aLayout );
218             break;
219 
220         case T_line:
221             item = new DS_DATA_ITEM( DS_DATA_ITEM::DS_SEGMENT );
222             parseGraphic( item );
223             aLayout->Append( item );
224             break;
225 
226         case T_rect:
227             item = new DS_DATA_ITEM( DS_DATA_ITEM::DS_RECT );
228             parseGraphic( item );
229             aLayout->Append( item );
230             break;
231 
232         case T_polygon:
233             item = new DS_DATA_ITEM_POLYGONS();
234             parsePolygon(  (DS_DATA_ITEM_POLYGONS*) item );
235             aLayout->Append( item );
236             break;
237 
238         case T_bitmap:
239             item = new DS_DATA_ITEM_BITMAP( NULL );
240             parseBitmap( (DS_DATA_ITEM_BITMAP*) item );
241             aLayout->Append( item );
242             break;
243 
244         case T_tbtext:
245             NeedSYMBOLorNUMBER();
246             item = new DS_DATA_ITEM_TEXT( convertLegacyVariableRefs( FromUTF8() ) );
247             parseText( (DS_DATA_ITEM_TEXT*) item );
248             aLayout->Append( item );
249             break;
250 
251         default:
252             Unexpected( CurText() );
253             break;
254         }
255     }
256 }
257 
parseHeader(T aHeaderType)258 void DRAWING_SHEET_PARSER::parseHeader( T aHeaderType )
259 {
260     // The older files had no versioning and their first token after the initial left parenthesis
261     // was a `page_layout` or `drawing_sheet` token. The newer files have versions and have a
262     // `kicad_wks` token instead.
263 
264     if( aHeaderType == T_kicad_wks || aHeaderType == T_drawing_sheet )
265     {
266         NeedLEFT();
267 
268         T tok = NextTok();
269 
270         if( tok == T_version )
271         {
272             m_requiredVersion = parseInt();
273 
274             if( m_requiredVersion > SEXPR_WORKSHEET_FILE_VERSION )
275                 throw FUTURE_FORMAT_ERROR( FromUTF8() );
276 
277             NeedRIGHT();
278         }
279         else
280         {
281             Expecting( T_version );
282         }
283 
284         // Ignore generator info.
285         NeedLEFT();
286         NeedSYMBOL();
287         NeedSYMBOL();
288         NeedRIGHT();
289     }
290     else
291     {
292         // We assign version 0 to files that were created before there was any versioning of
293         // worksheets. The below line is not strictly necessary, as `m_requiredVersion` is already
294         // initialized to 0 in the constructor.
295         m_requiredVersion = 0;
296     }
297 }
298 
parseSetup(DS_DATA_MODEL * aLayout)299 void DRAWING_SHEET_PARSER::parseSetup( DS_DATA_MODEL* aLayout )
300 {
301     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
302     {
303         switch( token )
304         {
305         case T_LEFT:
306             break;
307 
308         case T_linewidth:
309             aLayout->m_DefaultLineWidth = parseDouble();
310             NeedRIGHT();
311             break;
312 
313         case T_textsize:
314             aLayout->m_DefaultTextSize.x = parseDouble();
315             aLayout->m_DefaultTextSize.y = parseDouble();
316             NeedRIGHT();
317             break;
318 
319         case T_textlinewidth:
320             aLayout->m_DefaultTextThickness = parseDouble();
321             NeedRIGHT();
322             break;
323 
324         case T_left_margin:
325             aLayout->SetLeftMargin( parseDouble() );
326             NeedRIGHT();
327             break;
328 
329         case T_right_margin:
330             aLayout->SetRightMargin( parseDouble() );
331             NeedRIGHT();
332             break;
333 
334         case T_top_margin:
335             aLayout->SetTopMargin( parseDouble() );
336             NeedRIGHT();
337             break;
338 
339         case T_bottom_margin:
340             aLayout->SetBottomMargin( parseDouble() );
341             NeedRIGHT();
342             break;
343 
344         default:
345             Unexpected( CurText() );
346             break;
347         }
348     }
349 
350     // The file is well-formed.  If it has no further items, then that's the way the
351     // user wants it.
352     aLayout->AllowVoidList( true );
353 }
354 
355 
parsePolygon(DS_DATA_ITEM_POLYGONS * aItem)356 void DRAWING_SHEET_PARSER::parsePolygon( DS_DATA_ITEM_POLYGONS * aItem )
357 {
358     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
359     {
360         if( token == T_LEFT )
361             token = NextTok();
362 
363         switch( token )
364         {
365         case T_comment:
366             NeedSYMBOLorNUMBER();
367             aItem->m_Info =  FromUTF8();
368             NeedRIGHT();
369             break;
370 
371         case T_pos:
372             parseCoordinate( aItem->m_Pos );
373             break;
374 
375         case T_name:
376             NeedSYMBOLorNUMBER();
377             aItem->m_Name =  FromUTF8();
378             NeedRIGHT();
379             break;
380 
381         case T_option:
382             readOption( aItem );
383             break;
384 
385         case T_pts:
386             parsePolyOutline( aItem );
387             aItem->CloseContour();
388             break;
389 
390         case T_rotate:
391             aItem->m_Orient = parseDouble();
392             NeedRIGHT();
393             break;
394 
395         case T_repeat:
396             aItem->m_RepeatCount = parseInt( -1, 100 );
397             NeedRIGHT();
398             break;
399 
400         case T_incrx:
401             aItem->m_IncrementVector.x = parseDouble();
402             NeedRIGHT();
403             break;
404 
405         case T_incry:
406             aItem->m_IncrementVector.y = parseDouble();
407             NeedRIGHT();
408             break;
409 
410         case T_linewidth:
411             aItem->m_LineWidth = parseDouble();
412             NeedRIGHT();
413             break;
414 
415         default:
416             Unexpected( CurText() );
417             break;
418         }
419     }
420 
421     aItem->SetBoundingBox();
422 }
423 
parsePolyOutline(DS_DATA_ITEM_POLYGONS * aItem)424 void DRAWING_SHEET_PARSER::parsePolyOutline( DS_DATA_ITEM_POLYGONS * aItem )
425 {
426     DPOINT corner;
427 
428     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
429     {
430         if( token == T_LEFT )
431             token = NextTok();
432 
433         switch( token )
434         {
435         case T_xy:
436             corner.x = parseDouble();
437             corner.y = parseDouble();
438             aItem->AppendCorner( corner );
439             NeedRIGHT();
440             break;
441 
442         default:
443             Unexpected( CurText() );
444             break;
445         }
446     }
447 }
448 
449 
parseBitmap(DS_DATA_ITEM_BITMAP * aItem)450 void DRAWING_SHEET_PARSER::parseBitmap( DS_DATA_ITEM_BITMAP * aItem )
451 {
452     BITMAP_BASE* image = new BITMAP_BASE;
453     aItem->m_ImageBitmap = image;
454 
455     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
456     {
457         if( token == T_LEFT )
458             token = NextTok();
459 
460         switch( token )
461         {
462         case T_name:
463             NeedSYMBOLorNUMBER();
464             aItem->m_Name =  FromUTF8();
465             NeedRIGHT();
466             break;
467 
468         case T_pos:
469             parseCoordinate( aItem->m_Pos );
470             break;
471 
472         case T_repeat:
473             aItem->m_RepeatCount = parseInt( -1, 100 );
474             NeedRIGHT();
475             break;
476 
477         case T_incrx:
478             aItem->m_IncrementVector.x = parseDouble();
479             NeedRIGHT();
480             break;
481 
482         case T_incry:
483             aItem->m_IncrementVector.y = parseDouble();
484             NeedRIGHT();
485             break;
486 
487         case T_linewidth:
488             aItem->m_LineWidth = parseDouble();
489             NeedRIGHT();
490             break;
491 
492         case T_scale:
493             aItem->m_ImageBitmap->SetScale( parseDouble() );
494             NeedRIGHT();
495             break;
496 
497         case T_pngdata:
498             readPngdata( aItem );
499             break;
500 
501         case T_option:
502             readOption( aItem );
503             break;
504 
505         default:
506             Unexpected( CurText() );
507             break;
508         }
509     }
510 }
511 
readPngdata(DS_DATA_ITEM_BITMAP * aItem)512 void DRAWING_SHEET_PARSER::readPngdata( DS_DATA_ITEM_BITMAP * aItem )
513 {
514     std::string tmp;
515 
516     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
517     {
518         if( token == T_LEFT )
519             token = NextTok();
520 
521         switch( token )
522         {
523         case T_data:
524             NeedSYMBOLorNUMBER();
525             tmp += CurStr();
526             tmp += "\n";
527             NeedRIGHT();
528             break;
529 
530         default:
531             Unexpected( CurText() );
532             break;
533         }
534     }
535 
536     tmp += "EndData";
537 
538     wxString msg;
539     STRING_LINE_READER str_reader( tmp, wxT("Png kicad_wks data") );
540 
541     if( ! aItem->m_ImageBitmap->LoadData( str_reader, msg ) )
542         wxLogMessage(msg);
543 }
544 
545 
readOption(DS_DATA_ITEM * aItem)546 void DRAWING_SHEET_PARSER::readOption( DS_DATA_ITEM * aItem )
547 {
548     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
549     {
550         switch( token )
551         {
552         case T_page1only:  aItem->SetPage1Option( FIRST_PAGE_ONLY );  break;
553         case T_notonpage1: aItem->SetPage1Option( SUBSEQUENT_PAGES ); break;
554         default:           Unexpected( CurText() ); break;
555         }
556     }
557 }
558 
559 
parseGraphic(DS_DATA_ITEM * aItem)560 void DRAWING_SHEET_PARSER::parseGraphic( DS_DATA_ITEM * aItem )
561 {
562     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
563     {
564         if( token == T_LEFT )
565             token = NextTok();
566         else
567         {
568             // If another token than T_LEFT is read here, this is an error
569             // however, due to a old bug in kicad, the token T_end can be found
570             // without T_LEFT in a very few .wks files (perhaps only one in a demo).
571             // So this ugly hack disables the error detection.
572             if( token != T_end )
573                 Unexpected( CurText() );
574         }
575 
576         switch( token )
577         {
578         case T_comment:
579             NeedSYMBOLorNUMBER();
580             aItem->m_Info = FromUTF8();
581             NeedRIGHT();
582             break;
583 
584         case T_option:
585             readOption( aItem );
586             break;
587 
588         case T_name:
589             NeedSYMBOLorNUMBER();
590             aItem->m_Name = FromUTF8();
591             NeedRIGHT();
592             break;
593 
594         case T_start:
595             parseCoordinate( aItem->m_Pos );
596             break;
597 
598         case T_end:
599             parseCoordinate( aItem->m_End );
600             break;
601 
602         case T_repeat:
603             aItem->m_RepeatCount = parseInt( -1, 100 );
604             NeedRIGHT();
605             break;
606 
607         case T_incrx:
608             aItem->m_IncrementVector.x = parseDouble();
609             NeedRIGHT();
610             break;
611 
612         case T_incry:
613             aItem->m_IncrementVector.y = parseDouble();
614             NeedRIGHT();
615             break;
616 
617         case T_linewidth:
618             aItem->m_LineWidth = parseDouble();
619             NeedRIGHT();
620             break;
621 
622         default:
623             Unexpected( CurText() );
624             break;
625         }
626     }
627 }
628 
629 
parseText(DS_DATA_ITEM_TEXT * aItem)630 void DRAWING_SHEET_PARSER::parseText( DS_DATA_ITEM_TEXT* aItem )
631 {
632     if( m_requiredVersion < 20210606 )
633         aItem->m_TextBase = ConvertToNewOverbarNotation( aItem->m_TextBase );
634 
635     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
636     {
637         if( token == T_LEFT )
638             token = NextTok();
639 
640         switch( token )
641         {
642         case T_comment:
643             NeedSYMBOLorNUMBER();
644             aItem->m_Info =  FromUTF8();
645             NeedRIGHT();
646             break;
647 
648         case T_option:
649             readOption( aItem );
650             break;
651 
652         case T_name:
653             NeedSYMBOLorNUMBER();
654             aItem->m_Name =  FromUTF8();
655             NeedRIGHT();
656             break;
657 
658         case T_pos:
659             parseCoordinate( aItem->m_Pos );
660             break;
661 
662         case T_repeat:
663             aItem->m_RepeatCount = parseInt( -1, 100 );
664             NeedRIGHT();
665             break;
666 
667         case T_incrx:
668             aItem->m_IncrementVector.x = parseDouble();
669             NeedRIGHT();
670             break;
671 
672         case T_incry:
673             aItem->m_IncrementVector.y = parseDouble();
674             NeedRIGHT();
675             break;
676 
677         case T_incrlabel:
678             aItem->m_IncrementLabel = parseInt(INT_MIN, INT_MAX);
679             NeedRIGHT();
680             break;
681 
682         case T_maxlen:
683             aItem->m_BoundingBoxSize.x = parseDouble();
684             NeedRIGHT();
685             break;
686 
687         case T_maxheight:
688             aItem->m_BoundingBoxSize.y = parseDouble();
689             NeedRIGHT();
690             break;
691 
692         case T_font:
693             for( token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
694             {
695                 switch( token )
696                 {
697                 case T_LEFT:
698                     break;
699 
700                 case T_bold:
701                     aItem->m_Bold = true;
702                     break;
703 
704                 case T_italic:
705                     aItem->m_Italic = true;
706                     break;
707 
708                 case T_size:
709                     aItem->m_TextSize.x = parseDouble();
710                     aItem->m_TextSize.y = parseDouble();
711                     NeedRIGHT();
712                     break;
713 
714                 case T_linewidth:
715                     aItem->m_LineWidth = parseDouble();
716                     NeedRIGHT();
717                     break;
718 
719                 default:
720                     Unexpected( CurText() );
721                     break;
722                 }
723             }
724             break;
725 
726         case T_justify:
727             for( token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
728             {
729                 switch( token )
730                 {
731                 case T_center:
732                     aItem->m_Hjustify = GR_TEXT_HJUSTIFY_CENTER;
733                     aItem->m_Vjustify = GR_TEXT_VJUSTIFY_CENTER;
734                     break;
735 
736                 case T_left:
737                     aItem->m_Hjustify = GR_TEXT_HJUSTIFY_LEFT;
738                     break;
739 
740                 case T_right:
741                     aItem->m_Hjustify = GR_TEXT_HJUSTIFY_RIGHT;
742                     break;
743 
744                 case T_top:
745                     aItem->m_Vjustify = GR_TEXT_VJUSTIFY_TOP;
746                     break;
747 
748                 case T_bottom:
749                     aItem->m_Vjustify = GR_TEXT_VJUSTIFY_BOTTOM;
750                     break;
751 
752                 default:
753                     Unexpected( CurText() );
754                     break;
755                 }
756             }
757             break;
758 
759         case T_rotate:
760             aItem->m_Orient = parseDouble();
761             NeedRIGHT();
762             break;
763 
764         default:
765             Unexpected( CurText() );
766             break;
767         }
768     }
769 }
770 
771 // parse an expression like " 25 1 ltcorner)"
parseCoordinate(POINT_COORD & aCoord)772 void DRAWING_SHEET_PARSER::parseCoordinate( POINT_COORD& aCoord)
773 {
774     aCoord.m_Pos.x = parseDouble();
775     aCoord.m_Pos.y = parseDouble();
776 
777     for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
778     {
779         switch( token )
780         {
781         case T_ltcorner: aCoord.m_Anchor = LT_CORNER; break;
782         case T_lbcorner: aCoord.m_Anchor = LB_CORNER; break;
783         case T_rbcorner: aCoord.m_Anchor = RB_CORNER; break;
784         case T_rtcorner: aCoord.m_Anchor = RT_CORNER; break;
785         default:         Unexpected( CurText() ); break;
786         }
787     }
788 }
789 
parseInt()790 int DRAWING_SHEET_PARSER::parseInt()
791 {
792     T token = NextTok();
793 
794     if( token != T_NUMBER )
795         Expecting( T_NUMBER );
796 
797     return atoi( CurText() );
798 }
799 
parseInt(int aMin,int aMax)800 int DRAWING_SHEET_PARSER::parseInt( int aMin, int aMax )
801 {
802     int val = parseInt();
803 
804     if( val < aMin )
805         val = aMin;
806     else if( val > aMax )
807         val = aMax;
808 
809     return val;
810 }
811 
812 
parseDouble()813 double DRAWING_SHEET_PARSER::parseDouble()
814 {
815     T token = NextTok();
816 
817     if( token != T_NUMBER )
818         Expecting( T_NUMBER );
819 
820     double val = strtod( CurText(), NULL );
821 
822     return val;
823 }
824 
825 // defaultDrawingSheet is the default drawing sheet using the S expr.
826 extern const char defaultDrawingSheet[];
827 
SetDefaultLayout()828 void DS_DATA_MODEL::SetDefaultLayout()
829 {
830     SetPageLayout( defaultDrawingSheet, false, wxT( "default page" ) );
831 }
832 
833 // Returns defaultDrawingSheet as a string;
DefaultLayout()834 wxString DS_DATA_MODEL::DefaultLayout()
835 {
836     return wxString( defaultDrawingSheet );
837 }
838 
839 // emptyDrawingSheet is a "empty" drawing sheet using the S expr.
840 // there is a 0 length line to fool something somewhere.
841 extern const char emptyDrawingSheet[];
842 
SetEmptyLayout()843 void DS_DATA_MODEL::SetEmptyLayout()
844 {
845     SetPageLayout( emptyDrawingSheet, false, wxT( "empty page" ) );
846 }
847 
848 
EmptyLayout()849 wxString DS_DATA_MODEL::EmptyLayout()
850 {
851     return wxString( emptyDrawingSheet );
852 }
853 
854 
SetPageLayout(const char * aPageLayout,bool Append,const wxString & aSource)855 void DS_DATA_MODEL::SetPageLayout( const char* aPageLayout, bool Append, const wxString& aSource )
856 {
857     if( ! Append )
858         ClearList();
859 
860     DRAWING_SHEET_PARSER lp_parser( aPageLayout, wxT( "Sexpr_string" ) );
861 
862     try
863     {
864         lp_parser.Parse( this );
865     }
866     catch( const IO_ERROR& ioe )
867     {
868         wxLogMessage( ioe.What() );
869     }
870 }
871 
872 
LoadDrawingSheet(const wxString & aFullFileName,bool Append)873 bool DS_DATA_MODEL::LoadDrawingSheet( const wxString& aFullFileName, bool Append )
874 {
875     wxString fullFileName = aFullFileName;
876 
877     if( !Append )
878     {
879         if( fullFileName.IsEmpty() )
880             wxGetEnv( wxT( "KICAD_WKSFILE" ), &fullFileName );
881 
882         if( fullFileName.IsEmpty() )
883         {
884             SetDefaultLayout();
885             return true; // we assume its fine / default init
886         }
887 
888         if( !wxFileExists( fullFileName ) )
889         {
890             wxLogMessage( _( "Drawing sheet '%s' not found." ), fullFileName );
891             SetDefaultLayout();
892             return false;
893         }
894     }
895 
896     wxFFile wksFile( fullFileName, "rb" );
897 
898     if( ! wksFile.IsOpened() )
899     {
900         wxLogMessage( _( "Drawing sheet '%s' could not be opened." ), fullFileName );
901 
902         if( !Append )
903             SetDefaultLayout();
904 
905         return false;
906     }
907 
908     size_t filelen = wksFile.Length();
909     std::unique_ptr<char[]> buffer = std::make_unique<char[]>(filelen+10);
910 
911     if( wksFile.Read( buffer.get(), filelen ) != filelen )
912     {
913         wxLogMessage( _( "Drawing sheet '%s' was not fully read." ), fullFileName.GetData() );
914         return false;
915     }
916     else
917     {
918         buffer[filelen]=0;
919 
920         if( ! Append )
921             ClearList();
922 
923         DRAWING_SHEET_PARSER pl_parser( buffer.get(), fullFileName );
924 
925         try
926         {
927             pl_parser.Parse( this );
928         }
929         catch( const IO_ERROR& ioe )
930         {
931             wxLogMessage( ioe.What() );
932             return false;
933         }
934         catch( const std::bad_alloc& )
935         {
936             wxLogMessage( "Memory exhaustion reading drawing sheet" );
937             return false;
938         }
939     }
940 
941     return true;
942 }
943