MOAB: Mesh Oriented datABase  (version 5.4.1)
FileTokenizer.cpp
Go to the documentation of this file.
00001 /* *****************************************************************
00002     MESQUITE -- The Mesh Quality Improvement Toolkit
00003 
00004     Copyright 2004 Lawrence Livermore National Laboratory.  Under
00005     the terms of Contract B545069 with the University of Wisconsin --
00006     Madison, Lawrence Livermore National Laboratory retains certain
00007     rights in this software.
00008 
00009     This library is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU Lesser General Public
00011     License as published by the Free Software Foundation; either
00012     version 2.1 of the License, or (at your option) any later version.
00013 
00014     This library is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017     Lesser General Public License for more details.
00018 
00019     You should have received a copy of the GNU Lesser General Public License
00020     (lgpl.txt) along with this library; if not, write to the Free Software
00021     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 
00023     [email protected]
00024 
00025   ***************************************************************** */
00026 
00027 #include "FileTokenizer.hpp"
00028 #include "MsqError.hpp"
00029 #include <cstring>
00030 #include <cctype>
00031 #include <cstdlib>
00032 
00033 namespace MBMesquite
00034 {
00035 
00036 FileTokenizer::FileTokenizer( std::FILE* file_ptr )
00037     : filePtr( file_ptr ), nextToken( buffer ), bufferEnd( buffer ), lineNumber( 1 ), lastChar( '\0' )
00038 {
00039 }
00040 
00041 FileTokenizer::~FileTokenizer()
00042 {
00043     fclose( filePtr );
00044 }
00045 
00046 bool FileTokenizer::eof() const
00047 {
00048     return nextToken == bufferEnd && feof( filePtr );
00049 }
00050 
00051 const char* FileTokenizer::get_string( MsqError& err )
00052 {
00053     // If the whitepsace character marking the end of the
00054     // last token was a newline, increment the line count.
00055     if( lastChar == '\n' ) ++lineNumber;
00056 
00057     // Loop until either found the start of a token to return or have
00058     // reached the end of the file.
00059     for( ;; )
00060     {
00061         // If the buffer is empty, read more.
00062         if( nextToken == bufferEnd )
00063         {
00064             size_t count = fread( buffer, 1, sizeof( buffer ) - 1, filePtr );
00065             if( !count )
00066             {
00067                 if( feof( filePtr ) )
00068                     MSQ_SETERR( err )( "File truncated.\n", MsqError::PARSE_ERROR );
00069                 else
00070                     MSQ_SETERR( err )( MsqError::IO_ERROR );
00071                 return NULL;
00072             }
00073 
00074             nextToken = buffer;
00075             bufferEnd = buffer + count;
00076         }
00077 
00078         // If the current character is not a space, we've found a token.
00079         if( !isspace( *nextToken ) ) break;
00080 
00081         // If the current space character is a newline,
00082         // increment the line number count.
00083         if( *nextToken == '\n' ) ++lineNumber;
00084         ++nextToken;
00085     }
00086 
00087     // Store the start of the token in "result" and
00088     // advance "nextToken" to one past the end of the
00089     // token.
00090     char* result = nextToken;
00091     while( nextToken != bufferEnd && !isspace( *nextToken ) )
00092         ++nextToken;
00093 
00094     // If we have reached the end of the buffer without finding
00095     // a whitespace character terminating the token, we need to
00096     // read more from the file.  Only try once.  If the token is
00097     // too large to fit in the buffer, give up.
00098     if( nextToken == bufferEnd )
00099     {
00100         // Shift the (possibly) partial token to the start of the buffer.
00101         size_t remaining = bufferEnd - result;
00102         memmove( buffer, result, remaining );
00103         result    = buffer;
00104         nextToken = result + remaining;
00105 
00106         // Fill the remainder of the buffer after the token.
00107         size_t count = fread( nextToken, 1, sizeof( buffer ) - remaining - 1, filePtr );
00108         if( !count && !feof( filePtr ) )
00109         {
00110             MSQ_SETERR( err )( "I/O error.\n", MsqError::IO_ERROR );
00111             return NULL;
00112         }
00113         bufferEnd = nextToken + count;
00114 
00115         // Continue to advance nextToken until we find the space
00116         // terminating the token.
00117         while( nextToken != bufferEnd && !isspace( *nextToken ) )
00118             ++nextToken;
00119 
00120         if( nextToken == bufferEnd )  // EOF
00121         {
00122             *bufferEnd = '\0';
00123             ++bufferEnd;
00124         }
00125     }
00126 
00127     // Save terminating whitespace character (or NULL char if EOF).
00128     lastChar = *nextToken;
00129     // Put null in buffer to mark end of current token.
00130     *nextToken = '\0';
00131     // Advance nextToken to the next character to search next time.
00132     ++nextToken;
00133     return result;
00134 }
00135 
00136 bool FileTokenizer::get_double_internal( double& result, MsqError& err )
00137 {
00138     // Get a token
00139     const char *token_end, *token = get_string( err );
00140     if( MSQ_CHKERR( err ) ) return false;
00141 
00142     // Check for hex value -- on some platforms (e.g. Linux), strtod
00143     // will accept hex values, on others (e.g. Sun) it wil not.  Force
00144     // failure on hex numbers for consistancy.
00145     if( token[0] && token[1] && token[0] == '0' && toupper( token[1] ) == 'X' )
00146     {
00147         MSQ_SETERR( err )
00148         ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected number, got \"%s\"", line_number(), token );
00149         return false;
00150     }
00151 
00152     // Parse token as double
00153     result = strtod( token, (char**)&token_end );
00154 
00155     // If the one past the last char read by strtod is
00156     // not the NULL character terminating the string,
00157     // then parse failed.
00158     if( *token_end )
00159     {
00160         MSQ_SETERR( err )
00161         ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected number, got \"%s\"", line_number(), token );
00162         return false;
00163     }
00164 
00165     return true;
00166 }
00167 
00168 bool FileTokenizer::get_float_internal( float& result, MsqError& err )
00169 {
00170     double d = 0.0;
00171     get_double_internal( d, err );
00172     if( MSQ_CHKERR( err ) ) return false;
00173 
00174     result = (float)d;
00175     if( d != (double)result )
00176     {
00177         MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
00178         return false;
00179     }
00180 
00181     return true;
00182 }
00183 
00184 bool FileTokenizer::get_long_int_internal( long& result, MsqError& err )
00185 {
00186     // Get a token
00187     const char *token_end, *token = get_string( err );
00188     if( MSQ_CHKERR( err ) ) return false;
00189 
00190     // Parse token as long
00191     result = strtol( token, (char**)&token_end, 0 );
00192 
00193     // If the one past the last char read by strtol is
00194     // not the NULL character terminating the string,
00195     // then parse failed.
00196     if( *token_end )
00197     {
00198         MSQ_SETERR( err )
00199         ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected integer, got \"%s\"", line_number(), token );
00200         return false;
00201     }
00202 
00203     return true;
00204 }
00205 
00206 bool FileTokenizer::get_byte_internal( unsigned char& result, MsqError& err )
00207 {
00208     long i = 0;
00209     get_long_int_internal( i, err );
00210     if( MSQ_CHKERR( err ) ) return false;
00211 
00212     result = (unsigned char)i;
00213     if( i != (long)result )
00214     {
00215         MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
00216         return false;
00217     }
00218 
00219     return true;
00220 }
00221 
00222 bool FileTokenizer::get_short_int_internal( short& result, MsqError& err )
00223 {
00224     long i = 0;
00225     get_long_int_internal( i, err );
00226     if( MSQ_CHKERR( err ) ) return false;
00227 
00228     result = (short)i;
00229     if( i != (long)result )
00230     {
00231         MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
00232         return false;
00233     }
00234 
00235     return true;
00236 }
00237 
00238 bool FileTokenizer::get_integer_internal( int& result, MsqError& err )
00239 {
00240     long i = 0;
00241     get_long_int_internal( i, err );
00242     if( MSQ_CHKERR( err ) ) return false;
00243 
00244     result = (int)i;
00245     if( i != (long)result )
00246     {
00247         MSQ_SETERR( err )( MsqError::PARSE_ERROR, "Numberic overflow at line %d.", line_number() );
00248         return false;
00249     }
00250 
00251     return true;
00252 }
00253 
00254 bool FileTokenizer::get_boolean_internal( bool& result, MsqError& err )
00255 {
00256     // Get a token
00257     const char* token = get_string( err );
00258     if( MSQ_CHKERR( err ) ) return false;
00259 
00260     if( token[1] || ( token[0] != '0' && token[0] != '1' ) )
00261     {
00262         MSQ_SETERR( err )
00263         ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected 0 or 1, got \"%s\"", line_number(), token );
00264         return false;
00265     }
00266 
00267     result = token[0] == '1';
00268     return true;
00269 }
00270 
00271 bool FileTokenizer::get_floats( size_t count, float* array, MsqError& err )
00272 {
00273     for( size_t i = 0; i < count; ++i )
00274     {
00275         if( !get_float_internal( *array, err ) ) return false;
00276         ++array;
00277     }
00278     return true;
00279 }
00280 
00281 bool FileTokenizer::get_doubles( size_t count, double* array, MsqError& err )
00282 {
00283     for( size_t i = 0; i < count; ++i )
00284     {
00285         get_double_internal( *array, err );
00286         if( MSQ_CHKERR( err ) ) return false;
00287         ++array;
00288     }
00289     return true;
00290 }
00291 
00292 bool FileTokenizer::get_bytes( size_t count, unsigned char* array, MsqError& err )
00293 {
00294     for( size_t i = 0; i < count; ++i )
00295     {
00296         get_byte_internal( *array, err );
00297         if( MSQ_CHKERR( err ) ) return false;
00298         ++array;
00299     }
00300     return true;
00301 }
00302 
00303 bool FileTokenizer::get_short_ints( size_t count, short* array, MsqError& err )
00304 {
00305     for( size_t i = 0; i < count; ++i )
00306     {
00307         get_short_int_internal( *array, err );
00308         if( MSQ_CHKERR( err ) ) return false;
00309         ++array;
00310     }
00311     return true;
00312 }
00313 
00314 bool FileTokenizer::get_integers( size_t count, int* array, MsqError& err )
00315 {
00316     for( size_t i = 0; i < count; ++i )
00317     {
00318         get_integer_internal( *array, err );
00319         if( MSQ_CHKERR( err ) ) return false;
00320         ++array;
00321     }
00322     return true;
00323 }
00324 
00325 bool FileTokenizer::get_long_ints( size_t count, long* array, MsqError& err )
00326 {
00327     for( size_t i = 0; i < count; ++i )
00328     {
00329         get_long_int_internal( *array, err );
00330         if( MSQ_CHKERR( err ) ) return false;
00331         ++array;
00332     }
00333     return true;
00334 }
00335 
00336 bool FileTokenizer::get_booleans( size_t count, bool* array, MsqError& err )
00337 {
00338     for( size_t i = 0; i < count; ++i )
00339     {
00340         get_boolean_internal( *array, err );
00341         if( MSQ_CHKERR( err ) ) return false;
00342         ++array;
00343     }
00344     return true;
00345 }
00346 
00347 void FileTokenizer::unget_token()
00348 {
00349     if( nextToken - buffer < 2 ) return;
00350 
00351     --nextToken;
00352     *nextToken = lastChar;
00353     --nextToken;
00354     while( nextToken > buffer && *nextToken )
00355         --nextToken;
00356 
00357     if( !*nextToken ) ++nextToken;
00358 
00359     lastChar = '\0';
00360 }
00361 
00362 bool FileTokenizer::match_token( const char* str, MsqError& err )
00363 {
00364     // Get a token
00365     const char* token = get_string( err );
00366     if( MSQ_CHKERR( err ) ) return false;
00367 
00368     // Check if it matches
00369     if( 0 == strcmp( token, str ) ) return true;
00370 
00371     // Construct error message
00372     MSQ_SETERR( err )
00373     ( MsqError::PARSE_ERROR, "Syntax error at line %d: expected \"%s\", got \"%s\"", line_number(), str, token );
00374     return false;
00375 }  // namespace MBMesquite
00376 
00377 int FileTokenizer::match_token( const char* const* list, MsqError& err )
00378 {
00379     // Get a token
00380     const char* token = get_string( err );
00381     if( MSQ_CHKERR( err ) ) return false;
00382 
00383     // Check if it matches any input string
00384     const char* const* ptr;
00385     for( ptr = list; *ptr; ++ptr )
00386         if( 0 == strcmp( token, *ptr ) ) return ptr - list + 1;
00387 
00388     // No match, constuct error message
00389     std::string message( "Parsing error at line " );
00390     char lineno[16];
00391     sprintf( lineno, "%d", line_number() );
00392     message += lineno;
00393     message += ": expected one of {";
00394     for( ptr = list; *ptr; ++ptr )
00395     {
00396         message += " ";
00397         message += *ptr;
00398     }
00399     message += " } got \"";
00400     message += token;
00401     message += "\"";
00402     MSQ_SETERR( err )( message, MsqError::PARSE_ERROR );
00403     return false;
00404 }
00405 
00406 bool FileTokenizer::get_newline( MsqError& err )
00407 {
00408     if( lastChar == '\n' )
00409     {
00410         lastChar = ' ';
00411         ++lineNumber;
00412         return true;
00413     }
00414 
00415     // Loop until either we a) find a newline, b) find a non-whitespace
00416     // character or c) reach the end of the file.
00417     for( ;; )
00418     {
00419         // If the buffer is empty, read more.
00420         if( nextToken == bufferEnd )
00421         {
00422             size_t count = fread( buffer, 1, sizeof( buffer ), filePtr );
00423             if( !count )
00424             {
00425                 if( eof() )
00426                     MSQ_SETERR( err )( "File truncated.", MsqError::PARSE_ERROR );
00427                 else
00428                     MSQ_SETERR( err )( MsqError::IO_ERROR );
00429                 return false;
00430             }
00431 
00432             nextToken = buffer;
00433             bufferEnd = buffer + count;
00434         }
00435 
00436         // If the current character is not a space, the we've failed.
00437         if( !isspace( *nextToken ) )
00438         {
00439             MSQ_SETERR( err )
00440             ( MsqError::PARSE_ERROR, "Expected newline at line %d.", line_number() );
00441             return false;
00442         }
00443 
00444         // If the current space character is a newline,
00445         // increment the line number count.
00446         if( *nextToken == '\n' )
00447         {
00448             ++lineNumber;
00449             ++nextToken;
00450             lastChar = ' ';
00451             return true;
00452         }
00453         ++nextToken;
00454     }
00455 
00456     // should never reach this
00457     return false;
00458 }
00459 
00460 }  // namespace MBMesquite
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines