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) 2020-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 #include <cctype>
26 #include <iostream>
27 #include <sstream>
28 
29 #include <idf_common.h>
30 #include <idf_helpers.h>
31 
32 using namespace std;
33 using namespace IDF3;
34 
35 
FetchIDFLine(std::istream & aModel,std::string & aLine,bool & isComment,std::streampos & aFilePos)36 bool IDF3::FetchIDFLine( std::istream& aModel, std::string& aLine, bool& isComment,
37                          std::streampos& aFilePos )
38 {
39     aLine = "";
40     aFilePos = aModel.tellg();
41 
42     if( aModel.fail() )
43         return false;
44 
45     std::getline( aModel, aLine );
46 
47     isComment = false;
48 
49     // A comment begins with a '#' and must be the first character on the line
50     if( aLine[0] == '#' )
51     {
52         // opening '#' is stripped
53         isComment = true;
54         aLine.erase( aLine.begin() );
55     }
56 
57     // strip leading and trailing spaces
58     while( !aLine.empty() && isspace( *aLine.begin() ) )
59         aLine.erase( aLine.begin() );
60 
61     while( !aLine.empty() && isspace( *aLine.rbegin() ) )
62         aLine.erase( --aLine.end() );
63 
64     // a comment line may be empty to improve human readability
65     if( aLine.empty() && !isComment )
66         return false;
67 
68     return true;
69 }
70 
71 
GetIDFString(const std::string & aLine,std::string & aIDFString,bool & hasQuotes,int & aIndex)72 bool IDF3::GetIDFString( const std::string& aLine, std::string& aIDFString, bool& hasQuotes,
73                          int& aIndex )
74 {
75     // 1. drop all leading spaces
76     // 2. if the first character is '"', read until the next '"',
77     //    otherwise read until the next space or EOL.
78 
79     std::ostringstream ostr;
80 
81     int len = aLine.length();
82     int idx = aIndex;
83 
84     if( idx < 0 || idx >= len )
85         return false;
86 
87     while( idx < len && isspace( aLine[idx] ) )
88         ++idx;
89 
90     if( idx == len )
91     {
92         aIndex = idx;
93         return false;
94     }
95 
96     if( aLine[idx] == '"' )
97     {
98         hasQuotes = true;
99         ++idx;
100 
101         while( idx < len && aLine[idx] != '"' )
102             ostr << aLine[idx++];
103 
104         if( idx == len )
105         {
106             ERROR_IDF << "unterminated quote mark in line:\n";
107             aIndex = idx;
108             return false;
109         }
110 
111         ++idx;
112     }
113     else
114     {
115         hasQuotes = false;
116 
117         while( idx < len && !isspace( aLine[idx] ) )
118             ostr << aLine[idx++];
119 
120     }
121 
122     aIDFString = ostr.str();
123     aIndex = idx;
124 
125     return true;
126 }
127 
128 
129 // perform a comparison between a fixed token string and an input string.
130 // the token is assumed to be an upper case IDF token and the input string
131 // is data from an IDF file. Since IDF tokens are case-insensitive, we cannot
132 // assume anything about the case of the input string.
CompareToken(const char * aTokenString,const std::string & aInputString)133 bool IDF3::CompareToken( const char* aTokenString, const std::string& aInputString )
134 {
135     std::string::size_type i, j;
136     std::string bigToken = aInputString;
137     j = aInputString.length();
138 
139     for( i = 0; i < j; ++i )
140         bigToken[i] = std::toupper( bigToken[i] );
141 
142     if( !bigToken.compare( aTokenString ) )
143         return true;
144 
145     return false;
146 }
147 
148 
ParseOwner(const std::string & aToken,IDF3::KEY_OWNER & aOwner)149 bool IDF3::ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner )
150 {
151     if( CompareToken( "UNOWNED", aToken ) )
152     {
153         aOwner = UNOWNED;
154         return true;
155     }
156     else if( CompareToken( "ECAD", aToken ) )
157     {
158         aOwner = ECAD;
159         return true;
160     }
161     else if( CompareToken( "MCAD", aToken ) )
162     {
163         aOwner = MCAD;
164         return true;
165     }
166 
167     ERROR_IDF << "unrecognized IDF OWNER: '" << aToken << "'\n";
168 
169     return false;
170 }
171 
172 
ParseIDFLayer(const std::string & aToken,IDF3::IDF_LAYER & aLayer)173 bool IDF3::ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer )
174 {
175     if( CompareToken( "TOP", aToken ) )
176     {
177         aLayer = LYR_TOP;
178         return true;
179     }
180     else if( CompareToken( "BOTTOM", aToken ) )
181     {
182         aLayer = LYR_BOTTOM;
183         return true;
184     }
185     else if( CompareToken( "BOTH", aToken ) )
186     {
187         aLayer = LYR_BOTH;
188         return true;
189     }
190     else if( CompareToken( "INNER", aToken ) )
191     {
192         aLayer = LYR_INNER;
193         return true;
194     }
195     else if( CompareToken( "ALL", aToken ) )
196     {
197         aLayer = LYR_ALL;
198         return true;
199     }
200 
201     ERROR_IDF << "unrecognized IDF LAYER: '" << aToken << "'\n";
202 
203     aLayer = LYR_INVALID;
204     return false;
205 }
206 
207 
WriteLayersText(std::ostream & aBoardFile,IDF3::IDF_LAYER aLayer)208 bool IDF3::WriteLayersText( std::ostream& aBoardFile, IDF3::IDF_LAYER aLayer )
209 {
210     switch( aLayer )
211     {
212     case LYR_TOP:
213         aBoardFile << "TOP";
214         break;
215 
216     case LYR_BOTTOM:
217         aBoardFile << "BOTTOM";
218         break;
219 
220     case LYR_BOTH:
221         aBoardFile << "BOTH";
222         break;
223 
224     case LYR_INNER:
225         aBoardFile << "INNER";
226         break;
227 
228     case LYR_ALL:
229         aBoardFile << "ALL";
230         break;
231 
232     default:
233         do
234         {
235             std::ostringstream ostr;
236             ostr << "invalid IDF layer: " << aLayer;
237 
238             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
239         } while( 0 );
240 
241         break;
242     }
243 
244     return !aBoardFile.fail();
245 }
246 
247 
GetPlacementString(IDF3::IDF_PLACEMENT aPlacement)248 std::string IDF3::GetPlacementString( IDF3::IDF_PLACEMENT aPlacement )
249 {
250     switch( aPlacement )
251     {
252     case PS_UNPLACED:
253         return "UNPLACED";
254 
255     case PS_PLACED:
256         return "PLACED";
257 
258     case PS_MCAD:
259         return "MCAD";
260 
261     case PS_ECAD:
262         return "ECAD";
263 
264     default:
265         break;
266     }
267 
268     std::ostringstream ostr;
269     ostr << "[INVALID PLACEMENT VALUE]:" << aPlacement;
270 
271     return ostr.str();
272 }
273 
274 
GetLayerString(IDF3::IDF_LAYER aLayer)275 std::string IDF3::GetLayerString( IDF3::IDF_LAYER aLayer )
276 {
277     switch( aLayer )
278     {
279     case LYR_TOP:
280         return "TOP";
281 
282     case LYR_BOTTOM:
283         return "BOTTOM";
284 
285     case LYR_BOTH:
286         return "BOTH";
287 
288     case LYR_INNER:
289         return "INNER";
290 
291     case LYR_ALL:
292         return "ALL";
293 
294     default:
295         break;
296     }
297 
298     std::ostringstream ostr;
299     ostr << "[INVALID LAYER VALUE]:" << aLayer;
300 
301     return ostr.str();
302 }
303 
304 
GetOwnerString(IDF3::KEY_OWNER aOwner)305 std::string IDF3::GetOwnerString( IDF3::KEY_OWNER aOwner )
306 {
307     switch( aOwner )
308     {
309     case IDF3::UNOWNED:
310         return "UNOWNED";
311 
312     case IDF3::MCAD:
313         return "MCAD";
314 
315     case IDF3::ECAD:
316         return "ECAD";
317 
318     default:
319         break;
320     }
321 
322     ostringstream ostr;
323     ostr << "UNKNOWN: " << aOwner;
324 
325     return ostr.str();
326 }
327