Branch data Line data Source code
1 : : /* *****************************************************************
2 : : MESQUITE -- The Mesh Quality Improvement Toolkit
3 : :
4 : : Copyright 2004 Lawrence Livermore National Laboratory. Under
5 : : the terms of Contract B545069 with the University of Wisconsin --
6 : : Madison, Lawrence Livermore National Laboratory retains certain
7 : : rights in this software.
8 : :
9 : : This library is free software; you can redistribute it and/or
10 : : modify it under the terms of the GNU Lesser General Public
11 : : License as published by the Free Software Foundation; either
12 : : version 2.1 of the License, or (at your option) any later version.
13 : :
14 : : This library is distributed in the hope that it will be useful,
15 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : Lesser General Public License for more details.
18 : :
19 : : You should have received a copy of the GNU Lesser General Public License
20 : : (lgpl.txt) along with this library; if not, write to the Free Software
21 : : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 : :
23 : : [email protected]
24 : :
25 : : ***************************************************************** */
26 : :
27 : : #include "FileTokenizer.hpp"
28 : : #include "MsqError.hpp"
29 : : #include <cstring>
30 : : #include <cctype>
31 : : #include <cstdlib>
32 : :
33 : : namespace MBMesquite
34 : : {
35 : :
36 : 149 : FileTokenizer::FileTokenizer( std::FILE* file_ptr )
37 : 149 : : filePtr( file_ptr ), nextToken( buffer ), bufferEnd( buffer ), lineNumber( 1 ), lastChar( '\0' )
38 : : {
39 : 149 : }
40 : :
41 : 149 : FileTokenizer::~FileTokenizer()
42 : : {
43 : 149 : fclose( filePtr );
44 : 149 : }
45 : :
46 : 652 : bool FileTokenizer::eof() const
47 : : {
48 [ + + ][ + - ]: 652 : return nextToken == bufferEnd && feof( filePtr );
49 : : }
50 : :
51 : 994545 : const char* FileTokenizer::get_string( MsqError& err )
52 : : {
53 : : // If the whitepsace character marking the end of the
54 : : // last token was a newline, increment the line count.
55 [ + + ]: 994545 : if( lastChar == '\n' ) ++lineNumber;
56 : :
57 : : // Loop until either found the start of a token to return or have
58 : : // reached the end of the file.
59 : : for( ;; )
60 : : {
61 : : // If the buffer is empty, read more.
62 [ + + ]: 1049522 : if( nextToken == bufferEnd )
63 : : {
64 : 2136 : size_t count = fread( buffer, 1, sizeof( buffer ) - 1, filePtr );
65 [ + + ]: 2136 : if( !count )
66 : : {
67 [ + - ]: 7 : if( feof( filePtr ) )
68 [ + - ]: 7 : MSQ_SETERR( err )( "File truncated.\n", MsqError::PARSE_ERROR );
69 : : else
70 [ # # ]: 0 : MSQ_SETERR( err )( MsqError::IO_ERROR );
71 : 7 : return NULL;
72 : : }
73 : :
74 : 2129 : nextToken = buffer;
75 : 2129 : bufferEnd = buffer + count;
76 : : }
77 : :
78 : : // If the current character is not a space, we've found a token.
79 [ + + ]: 1049515 : if( !isspace( *nextToken ) ) break;
80 : :
81 : : // If the current space character is a newline,
82 : : // increment the line number count.
83 [ + + ]: 54977 : if( *nextToken == '\n' ) ++lineNumber;
84 : 54977 : ++nextToken;
85 : 54977 : }
86 : :
87 : : // Store the start of the token in "result" and
88 : : // advance "nextToken" to one past the end of the
89 : : // token.
90 : 994538 : char* result = nextToken;
91 [ + + ][ + + ]: 5189349 : while( nextToken != bufferEnd && !isspace( *nextToken ) )
92 : 4194811 : ++nextToken;
93 : :
94 : : // If we have reached the end of the buffer without finding
95 : : // a whitespace character terminating the token, we need to
96 : : // read more from the file. Only try once. If the token is
97 : : // too large to fit in the buffer, give up.
98 [ + + ]: 994538 : if( nextToken == bufferEnd )
99 : : {
100 : : // Shift the (possibly) partial token to the start of the buffer.
101 : 8327 : size_t remaining = bufferEnd - result;
102 : 8327 : memmove( buffer, result, remaining );
103 : 8327 : result = buffer;
104 : 8327 : nextToken = result + remaining;
105 : :
106 : : // Fill the remainder of the buffer after the token.
107 : 8327 : size_t count = fread( nextToken, 1, sizeof( buffer ) - remaining - 1, filePtr );
108 [ - + ][ # # ]: 8327 : if( !count && !feof( filePtr ) )
[ - + ]
109 : : {
110 [ # # ]: 0 : MSQ_SETERR( err )( "I/O error.\n", MsqError::IO_ERROR );
111 : 0 : return NULL;
112 : : }
113 : 8327 : bufferEnd = nextToken + count;
114 : :
115 : : // Continue to advance nextToken until we find the space
116 : : // terminating the token.
117 [ + - ][ + + ]: 58766 : while( nextToken != bufferEnd && !isspace( *nextToken ) )
118 : 50439 : ++nextToken;
119 : :
120 [ - + ]: 8327 : if( nextToken == bufferEnd ) // EOF
121 : : {
122 : 0 : *bufferEnd = '\0';
123 : 8327 : ++bufferEnd;
124 : : }
125 : : }
126 : :
127 : : // Save terminating whitespace character (or NULL char if EOF).
128 : 994538 : lastChar = *nextToken;
129 : : // Put null in buffer to mark end of current token.
130 : 994538 : *nextToken = '\0';
131 : : // Advance nextToken to the next character to search next time.
132 : 994538 : ++nextToken;
133 : 994545 : return result;
134 : : }
135 : :
136 : 234181 : bool FileTokenizer::get_double_internal( double& result, MsqError& err )
137 : : {
138 : : // Get a token
139 [ + - ]: 234181 : const char *token_end, *token = get_string( err );
140 [ + - ][ - + ]: 234181 : if( MSQ_CHKERR( err ) ) return false;
[ # # ][ # # ]
[ - + ]
141 : :
142 : : // Check for hex value -- on some platforms (e.g. Linux), strtod
143 : : // will accept hex values, on others (e.g. Sun) it wil not. Force
144 : : // failure on hex numbers for consistancy.
145 [ + - ][ + + ]: 234181 : if( token[0] && token[1] && token[0] == '0' && toupper( token[1] ) == 'X' )
[ + + ][ - + ]
146 : : {
147 : : MSQ_SETERR( err )
148 [ # # ][ # # ]: 0 : ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected number, got \"%s\"", line_number(), token );
[ # # ]
149 : 0 : return false;
150 : : }
151 : :
152 : : // Parse token as double
153 : 234181 : result = strtod( token, (char**)&token_end );
154 : :
155 : : // If the one past the last char read by strtod is
156 : : // not the NULL character terminating the string,
157 : : // then parse failed.
158 [ - + ]: 234181 : if( *token_end )
159 : : {
160 : : MSQ_SETERR( err )
161 [ # # ][ # # ]: 0 : ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected number, got \"%s\"", line_number(), token );
[ # # ]
162 : 0 : return false;
163 : : }
164 : :
165 : 234181 : return true;
166 : : }
167 : :
168 : 0 : bool FileTokenizer::get_float_internal( float& result, MsqError& err )
169 : : {
170 : 0 : double d = 0.0;
171 [ # # ]: 0 : get_double_internal( d, err );
172 [ # # ][ # # ]: 0 : if( MSQ_CHKERR( err ) ) return false;
[ # # ][ # # ]
[ # # ]
173 : :
174 : 0 : result = (float)d;
175 [ # # ]: 0 : if( d != (double)result )
176 : : {
177 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
[ # # ]
178 : 0 : return false;
179 : : }
180 : :
181 : 0 : return true;
182 : : }
183 : :
184 : 752871 : bool FileTokenizer::get_long_int_internal( long& result, MsqError& err )
185 : : {
186 : : // Get a token
187 [ + - ]: 752871 : const char *token_end, *token = get_string( err );
188 [ + - ][ - + ]: 752871 : if( MSQ_CHKERR( err ) ) return false;
[ # # ][ # # ]
[ - + ]
189 : :
190 : : // Parse token as long
191 : 752871 : result = strtol( token, (char**)&token_end, 0 );
192 : :
193 : : // If the one past the last char read by strtol is
194 : : // not the NULL character terminating the string,
195 : : // then parse failed.
196 [ - + ]: 752871 : if( *token_end )
197 : : {
198 : : MSQ_SETERR( err )
199 [ # # ][ # # ]: 0 : ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected integer, got \"%s\"", line_number(), token );
[ # # ]
200 : 0 : return false;
201 : : }
202 : :
203 : 752871 : return true;
204 : : }
205 : :
206 : 0 : bool FileTokenizer::get_byte_internal( unsigned char& result, MsqError& err )
207 : : {
208 : 0 : long i = 0;
209 [ # # ]: 0 : get_long_int_internal( i, err );
210 [ # # ][ # # ]: 0 : if( MSQ_CHKERR( err ) ) return false;
[ # # ][ # # ]
[ # # ]
211 : :
212 : 0 : result = (unsigned char)i;
213 [ # # ]: 0 : if( i != (long)result )
214 : : {
215 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
[ # # ]
216 : 0 : return false;
217 : : }
218 : :
219 : 0 : return true;
220 : : }
221 : :
222 : 0 : bool FileTokenizer::get_short_int_internal( short& result, MsqError& err )
223 : : {
224 : 0 : long i = 0;
225 [ # # ]: 0 : get_long_int_internal( i, err );
226 [ # # ][ # # ]: 0 : if( MSQ_CHKERR( err ) ) return false;
[ # # ][ # # ]
[ # # ]
227 : :
228 : 0 : result = (short)i;
229 [ # # ]: 0 : if( i != (long)result )
230 : : {
231 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
[ # # ]
232 : 0 : return false;
233 : : }
234 : :
235 : 0 : return true;
236 : : }
237 : :
238 : 47140 : bool FileTokenizer::get_integer_internal( int& result, MsqError& err )
239 : : {
240 : 47140 : long i = 0;
241 [ + - ]: 47140 : get_long_int_internal( i, err );
242 [ + - ][ - + ]: 47140 : if( MSQ_CHKERR( err ) ) return false;
[ # # ][ # # ]
[ - + ]
243 : :
244 : 47140 : result = (int)i;
245 [ - + ]: 47140 : if( i != (long)result )
246 : : {
247 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
[ # # ]
248 : 0 : return false;
249 : : }
250 : :
251 : 47140 : return true;
252 : : }
253 : :
254 : 4999 : bool FileTokenizer::get_boolean_internal( bool& result, MsqError& err )
255 : : {
256 : : // Get a token
257 : 4999 : const char* token = get_string( err );
258 [ - + ][ # # ]: 4999 : if( MSQ_CHKERR( err ) ) return false;
[ - + ]
259 : :
260 [ + - ][ + + ]: 4999 : if( token[1] || ( token[0] != '0' && token[0] != '1' ) )
[ - + ]
261 : : {
262 : : MSQ_SETERR( err )
263 [ # # ]: 0 : ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected 0 or 1, got \"%s\"", line_number(), token );
264 : 0 : return false;
265 : : }
266 : :
267 : 4999 : result = token[0] == '1';
268 : 4999 : return true;
269 : : }
270 : :
271 : 0 : bool FileTokenizer::get_floats( size_t count, float* array, MsqError& err )
272 : : {
273 [ # # ]: 0 : for( size_t i = 0; i < count; ++i )
274 : : {
275 [ # # ]: 0 : if( !get_float_internal( *array, err ) ) return false;
276 : 0 : ++array;
277 : : }
278 : 0 : return true;
279 : : }
280 : :
281 : 65782 : bool FileTokenizer::get_doubles( size_t count, double* array, MsqError& err )
282 : : {
283 [ + + ]: 299963 : for( size_t i = 0; i < count; ++i )
284 : : {
285 : 234181 : get_double_internal( *array, err );
286 [ - + ][ # # ]: 234181 : if( MSQ_CHKERR( err ) ) return false;
[ - + ]
287 : 234181 : ++array;
288 : : }
289 : 65782 : return true;
290 : : }
291 : :
292 : 0 : bool FileTokenizer::get_bytes( size_t count, unsigned char* array, MsqError& err )
293 : : {
294 [ # # ]: 0 : for( size_t i = 0; i < count; ++i )
295 : : {
296 : 0 : get_byte_internal( *array, err );
297 [ # # ][ # # ]: 0 : if( MSQ_CHKERR( err ) ) return false;
[ # # ]
298 : 0 : ++array;
299 : : }
300 : 0 : return true;
301 : : }
302 : :
303 : 0 : bool FileTokenizer::get_short_ints( size_t count, short* array, MsqError& err )
304 : : {
305 [ # # ]: 0 : for( size_t i = 0; i < count; ++i )
306 : : {
307 : 0 : get_short_int_internal( *array, err );
308 [ # # ][ # # ]: 0 : if( MSQ_CHKERR( err ) ) return false;
[ # # ]
309 : 0 : ++array;
310 : : }
311 : 0 : return true;
312 : : }
313 : :
314 : 79 : bool FileTokenizer::get_integers( size_t count, int* array, MsqError& err )
315 : : {
316 [ + + ]: 47219 : for( size_t i = 0; i < count; ++i )
317 : : {
318 : 47140 : get_integer_internal( *array, err );
319 [ - + ][ # # ]: 47140 : if( MSQ_CHKERR( err ) ) return false;
[ - + ]
320 : 47140 : ++array;
321 : : }
322 : 79 : return true;
323 : : }
324 : :
325 : 349537 : bool FileTokenizer::get_long_ints( size_t count, long* array, MsqError& err )
326 : : {
327 [ + + ]: 1055268 : for( size_t i = 0; i < count; ++i )
328 : : {
329 : 705731 : get_long_int_internal( *array, err );
330 [ - + ][ # # ]: 705731 : if( MSQ_CHKERR( err ) ) return false;
[ - + ]
331 : 705731 : ++array;
332 : : }
333 : 349537 : return true;
334 : : }
335 : :
336 : 75 : bool FileTokenizer::get_booleans( size_t count, bool* array, MsqError& err )
337 : : {
338 [ + + ]: 5074 : for( size_t i = 0; i < count; ++i )
339 : : {
340 : 4999 : get_boolean_internal( *array, err );
341 [ - + ][ # # ]: 4999 : if( MSQ_CHKERR( err ) ) return false;
[ - + ]
342 : 4999 : ++array;
343 : : }
344 : 75 : return true;
345 : : }
346 : :
347 : 316 : void FileTokenizer::unget_token()
348 : : {
349 [ - + ]: 316 : if( nextToken - buffer < 2 ) return;
350 : :
351 : 316 : --nextToken;
352 : 316 : *nextToken = lastChar;
353 : 316 : --nextToken;
354 [ + - ][ + + ]: 3160 : while( nextToken > buffer && *nextToken )
355 : 2844 : --nextToken;
356 : :
357 [ + - ]: 316 : if( !*nextToken ) ++nextToken;
358 : :
359 : 316 : lastChar = '\0';
360 : : }
361 : :
362 : 781 : bool FileTokenizer::match_token( const char* str, MsqError& err )
363 : : {
364 : : // Get a token
365 : 781 : const char* token = get_string( err );
366 [ - + ][ # # ]: 781 : if( MSQ_CHKERR( err ) ) return false;
[ - + ]
367 : :
368 : : // Check if it matches
369 [ + + ]: 781 : if( 0 == strcmp( token, str ) ) return true;
370 : :
371 : : // Construct error message
372 : : MSQ_SETERR( err )
373 [ + - ]: 149 : ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected \"%s\", got \"%s\"", line_number(), str, token );
374 : 781 : return false;
375 : : } // namespace MBMesquite
376 : :
377 : 1158 : int FileTokenizer::match_token( const char* const* list, MsqError& err )
378 : : {
379 : : // Get a token
380 [ + - ]: 1158 : const char* token = get_string( err );
381 [ + - ][ + + ]: 1158 : if( MSQ_CHKERR( err ) ) return false;
[ + - ][ + - ]
[ + + ]
382 : :
383 : : // Check if it matches any input string
384 : : const char* const* ptr;
385 [ + + ]: 3553 : for( ptr = list; *ptr; ++ptr )
386 [ + + ]: 3540 : if( 0 == strcmp( token, *ptr ) ) return ptr - list + 1;
387 : :
388 : : // No match, constuct error message
389 [ + - ]: 13 : std::string message( "Parsing error at line " );
390 : : char lineno[16];
391 [ + - ]: 13 : sprintf( lineno, "%d", line_number() );
392 [ + - ]: 13 : message += lineno;
393 [ + - ]: 13 : message += ": expected one of {";
394 [ + + ]: 52 : for( ptr = list; *ptr; ++ptr )
395 : : {
396 [ + - ]: 39 : message += " ";
397 [ + - ]: 39 : message += *ptr;
398 : : }
399 [ + - ]: 13 : message += " } got \"";
400 [ + - ]: 13 : message += token;
401 [ + - ]: 13 : message += "\"";
402 [ + - ][ + - ]: 13 : MSQ_SETERR( err )( message, MsqError::PARSE_ERROR );
403 : 1158 : return false;
404 : : }
405 : :
406 : 447 : bool FileTokenizer::get_newline( MsqError& err )
407 : : {
408 [ + + ]: 447 : if( lastChar == '\n' )
409 : : {
410 : 446 : lastChar = ' ';
411 : 446 : ++lineNumber;
412 : 446 : return true;
413 : : }
414 : :
415 : : // Loop until either we a) find a newline, b) find a non-whitespace
416 : : // character or c) reach the end of the file.
417 : : for( ;; )
418 : : {
419 : : // If the buffer is empty, read more.
420 [ - + ]: 1 : if( nextToken == bufferEnd )
421 : : {
422 : 0 : size_t count = fread( buffer, 1, sizeof( buffer ), filePtr );
423 [ # # ]: 0 : if( !count )
424 : : {
425 [ # # ]: 0 : if( eof() )
426 [ # # ]: 0 : MSQ_SETERR( err )( "File truncated.", MsqError::PARSE_ERROR );
427 : : else
428 [ # # ]: 0 : MSQ_SETERR( err )( MsqError::IO_ERROR );
429 : 0 : return false;
430 : : }
431 : :
432 : 0 : nextToken = buffer;
433 : 0 : bufferEnd = buffer + count;
434 : : }
435 : :
436 : : // If the current character is not a space, the we've failed.
437 [ - + ]: 1 : if( !isspace( *nextToken ) )
438 : : {
439 : : MSQ_SETERR( err )
440 [ # # ]: 0 : ( MsqError::PARSE_ERROR, "Expected newline at line %d.", line_number() );
441 : 0 : return false;
442 : : }
443 : :
444 : : // If the current space character is a newline,
445 : : // increment the line number count.
446 [ + - ]: 1 : if( *nextToken == '\n' )
447 : : {
448 : 1 : ++lineNumber;
449 : 1 : ++nextToken;
450 : 1 : lastChar = ' ';
451 : 1 : return true;
452 : : }
453 : 0 : ++nextToken;
454 : 447 : }
455 : :
456 : : // should never reach this
457 : : return false;
458 : : }
459 : :
460 : : } // namespace MBMesquite
|