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