1 #define SC_CORE_EXPORT
2 #define SC_DAI_EXPORT
3 
4 #include <algorithm>
5 #include <set>
6 #include <string>
7 #include <assert.h>
8 
9 #include "Registry.h"
10 #include "sc_strtoull.h"
11 #include "sdaiApplication_instance.h"
12 #include "read_func.h"
13 #include "SdaiSchemaInit.h"
14 #include "STEPcomplex.h"
15 
16 #include "sectionReader.h"
17 #include "lazyFileReader.h"
18 #include "lazyInstMgr.h"
19 #include "lazyTypes.h"
20 #include "instMgrHelper.h"
21 
22 #include "current_function.hpp"
23 
sectionReader(lazyFileReader * parent,std::ifstream & file,std::streampos start,sectionID sid)24 sectionReader::sectionReader( lazyFileReader * parent, std::ifstream & file, std::streampos start, sectionID sid ):
25     _lazyFile( parent ), _file( file ), _sectionStart( start ), _sectionID( sid ) {
26     _fileID = _lazyFile->ID();
27     _error = new ErrorDescriptor();
28 }
29 
30 
findNormalString(const std::string & str,bool semicolon)31 std::streampos sectionReader::findNormalString( const std::string & str, bool semicolon ) {
32     std::streampos found = -1, startPos = _file.tellg(), nextTry = startPos;
33     int i = 0, l = str.length();
34     char c;
35 
36     //i is reset every time a character doesn't match; if i == l, this means that we've found the entire string
37     while( i < l || semicolon ) {
38         skipWS();
39         c = _file.get();
40         if( ( i == l ) && ( semicolon ) ) {
41             if( c == ';' ) {
42                 break;
43             } else {
44                 i = 0;
45                 _file.seekg( nextTry );
46                 continue;
47             }
48         }
49         if( c == '\'' ) {
50             //push past string
51             _file.unget();
52             GetLiteralStr( _file, _lazyFile->getInstMgr()->getErrorDesc() );
53         }
54         if( ( c == '/' ) && ( _file.peek() == '*' ) ) {
55             //push past comment
56             findNormalString( "*/" );
57         }
58         if( str[i] == c ) {
59             i++;
60             if( i == 1 ) {
61                 nextTry = _file.tellg();
62             }
63         } else {
64             if( !_file.good() ) {
65                 break;
66             }
67             if( i >= 1 ) {
68                 _file.seekg( nextTry );
69             }
70             i = 0;
71         }
72     }
73     if( i == l ) {
74         found = _file.tellg();
75     }
76     if( _file.is_open() && _file.good() ) {
77         return found;
78     } else {
79         return -1;
80     }
81 }
82 
83 
84 //NOTE different behavior than const char * GetKeyword( istream & in, const char * delims, ErrorDescriptor & err ) in read_func.cc
85 // returns pointer to the contents of a static std::string
getDelimitedKeyword(const char * delimiters)86 const char * sectionReader::getDelimitedKeyword( const char * delimiters ) {
87     static std::string str;
88     char c;
89     str.clear();
90     str.reserve( 100 );
91     skipWS();
92     while( c = _file.get(), _file.good() ) {
93         if( c == '-' || c == '_' || isupper( c ) || isdigit( c ) ||
94                 ( c == '!' && str.length() == 0 ) ) {
95             str.append( 1, c );
96         } else if( ( c == '/' ) && ( _file.peek() == '*' ) && ( str.length() == 0 ) ) {
97             //push past comment
98             findNormalString( "*/" );
99             skipWS();
100             continue;
101         } else {
102             _file.putback( c );
103             break;
104         }
105     }
106     c = _file.peek();
107     if( !strchr( delimiters, c ) ) {
108         std::cerr << SC_CURRENT_FUNCTION << ": missing delimiter. Found " << c << ", expected one of " << delimiters << " at end of keyword " << str << ". File offset: " << _file.tellg() << std::endl;
109         abort();
110     }
111     return str.c_str();
112 }
113 
114 /// search forward in the file for the end of the instance. Start position should
115 /// be the opening parenthesis; otherwise, it is likely to fail.
116 ///NOTE *must* check return value!
seekInstanceEnd(instanceRefs ** refs)117 std::streampos sectionReader::seekInstanceEnd( instanceRefs ** refs ) {
118     char c;
119     int parenDepth = 0;
120     while( c = _file.get(), _file.good() ) {
121         switch( c ) {
122             case '(':
123                 parenDepth++;
124                 break;
125             case '/':
126                 if( _file.peek() == '*' ) {
127                     findNormalString( "*/" );
128                 } else {
129                     return -1;
130                 }
131                 break;
132             case '\'':
133                 _file.unget();
134                 GetLiteralStr( _file, _lazyFile->getInstMgr()->getErrorDesc() );
135                 break;
136             case '=':
137                 return -1;
138             case '#':
139                 skipWS();
140                 if( isdigit( _file.peek() ) ) {
141                     if( refs != 0 ) {
142                         if( ! * refs ) {
143                             *refs = new std::vector< instanceID >;
144                         }
145                         instanceID n;
146                         _file >> n;
147                         ( * refs )->push_back( n );
148                     }
149                 } else {
150                     return -1;
151                 }
152                 break;
153             case ')':
154                 if( --parenDepth == 0 ) {
155                     skipWS();
156                     if( _file.get() == ';' ) {
157                         return _file.tellg();
158                     } else {
159                         _file.unget();
160                     }
161                 }
162             default:
163                 break;
164         }
165     }
166     return -1;
167     //NOTE - old way: return findNormalString( ")", true );
168     // old memory consumption: 673728kb; User CPU time: 35480ms; System CPU time: 17710ms (with 266MB catia-ferrari-sharknose.stp)
169     // new memory: 673340kb; User CPU time: 29890ms; System CPU time: 11650ms
170 }
171 
locateAllInstances()172 void sectionReader::locateAllInstances() {
173     namedLazyInstance inst;
174     while( inst = nextInstance(), ( _file.good() ) && ( inst.loc.begin > 0 ) ) {
175         _lazyFile->getInstMgr()->addLazyInstance( inst );
176     }
177 }
178 
readInstanceNumber()179 instanceID sectionReader::readInstanceNumber() {
180     char c;
181     size_t digits = 0;
182     instanceID id = 0;
183 
184     //find instance number ("# nnnn ="), where ' ' is any whitespace found by isspace()
185     skipWS();
186     c = _file.get();
187     if( ( c == '/' ) && ( _file.peek() == '*' ) ) {
188         findNormalString( "*/" );
189     } else {
190         _file.unget();
191     }
192     skipWS();
193     c = _file.get();
194     if( c != '#' ) {
195         return 0;
196     }
197     skipWS();
198 
199     // The largest instance ID yet supported is the maximum value of unsigned long long int
200     assert( std::numeric_limits<instanceID>::max() <= std::numeric_limits<unsigned long long int>::max() );
201 
202     size_t instanceIDLength = std::numeric_limits<instanceID>::digits10 + 1;
203     char * buffer = new char( instanceIDLength + 1 ); // +1 for the terminating character
204 
205     std::stringstream errorMsg;
206 
207     do {
208         c = _file.get();
209         if( isdigit( c ) ) {
210             buffer[ digits ] = c; //copy the charcter into the buffer
211             digits++;
212 
213         } else {
214             _file.unget();
215             break;
216         }
217 
218         if( digits > instanceIDLength ) {
219             errorMsg << "A very large instance ID of string length greater then " << instanceIDLength << " found. Skipping data section " << _sectionID << ".";
220 
221             _error->GreaterSeverity( SEVERITY_INPUT_ERROR );
222             _error->UserMsg( "A very large instance ID encountered" );
223             _error->DetailMsg( errorMsg.str() );
224 
225             delete buffer;
226             return 0;
227         }
228 
229     } while( _file.good() );
230     buffer[ digits ] = '\0'; //Append the terminating character
231     skipWS();
232 
233     if( _file.good() && ( digits > 0 ) && ( _file.get() == '=' ) ) {
234         id = strtoull( buffer, NULL, 10);
235         if( id == std::numeric_limits<instanceID>::max() ) {
236             //Handling those cases where although the number of digits is equal, but the id value is greater then equal to the maximum allowed value.
237             errorMsg << "A very large instance ID caused an overflow. Skipping data section " << _sectionID << ".";
238 
239             _error->GreaterSeverity( SEVERITY_INPUT_ERROR );
240             _error->UserMsg( "A very large instance ID encountered" );
241             _error->DetailMsg( errorMsg.str() );
242         }
243 
244         assert( id > 0 );
245     }
246     delete buffer;
247     return id;
248 }
249 
250 /** load an instance and return a pointer to it.
251  * side effect: recursively loads any instances the specified instance depends upon
252  */
getRealInstance(const Registry * reg,long int begin,instanceID instance,const std::string & typeName,const std::string & schName,bool header)253 SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg, long int begin, instanceID instance,
254         const std::string & typeName, const std::string & schName, bool header ) {
255     char c;
256     const char * tName = 0, * sName = 0; //these are necessary since typeName and schName are const
257     std::string comment;
258     Severity sev = SEVERITY_NULL;
259     SDAI_Application_instance * inst = 0;
260 
261     tName = typeName.c_str();
262     if( schName.size() > 0 ) {
263         sName = schName.c_str();
264     } else if( !header ) {
265         SdaiFile_schema * fs = dynamic_cast< SdaiFile_schema * >( _lazyFile->getHeaderInstances()->find( 3 ) );
266         if( fs ) {
267             StringNode * sn = ( StringNode * ) fs->schema_identifiers_()->GetHead();
268             if( sn ) {
269                 sName = sn->value.c_str();
270                 if( sn->NextNode() ) {
271                     std::cerr << "Warning - multiple schema names found. Only searching with first one." << std::endl;
272                 }
273             }
274         } else {
275             std::cerr << "Warning - no schema names found; the file is probably invalid. Looking for typeName in any loaded schema." << std::endl;
276         }
277     }
278 
279     _file.seekg( begin );
280     skipWS();
281     ReadTokenSeparator( _file, &comment );
282     if( !header ) {
283         findNormalString( "=" );
284     }
285     skipWS();
286     ReadTokenSeparator( _file, &comment );
287     c = _file.peek();
288     switch( c ) {
289         case '&':
290             std::cerr << "Can't handle scope instances. Skipping #" << instance << ", offset " << _file.tellg() << std::endl;
291             // sev = CreateScopeInstances( in, &scopelist );
292             break;
293         case '(':
294             inst = CreateSubSuperInstance( reg, instance, sev );
295             break;
296         case '!':
297             std::cerr << "Can't handle user-defined instances. Skipping #" << instance << ", offset " << _file.tellg() << std::endl;
298             break;
299         default:
300             if( ( !header ) && ( typeName.size() == 0 ) ) {
301                 tName = getDelimitedKeyword( ";( /\\" );
302             }
303             inst = reg->ObjCreate( tName, sName );
304             break;
305     }
306 
307     if( inst != & NilSTEPentity ) {
308         if( !comment.empty() ) {
309             inst->AddP21Comment( comment );
310         }
311         assert( inst->getEDesc() );
312         _file.seekg( begin );
313         findNormalString( "(" );
314         _file.unget();
315         sev = inst->STEPread( instance, 0, _lazyFile->getInstMgr()->getAdapter(), _file, sName, true, false );
316         //TODO do something with 'sev'
317         inst->InitIAttrs();
318     }
319     return inst;
320 }
321 
CreateSubSuperInstance(const Registry * reg,instanceID fileid,Severity & sev)322 STEPcomplex * sectionReader::CreateSubSuperInstance( const Registry * reg, instanceID fileid, Severity & sev ) {
323     std::string buf;
324     ErrorDescriptor err;
325     std::vector<std::string *> typeNames;
326     _file.get(); //move past the first '('
327     skipWS();
328     while( _file.good() && ( _file.peek() != ')' ) ) {
329         typeNames.push_back( new std::string( getDelimitedKeyword( ";( /\\\n" ) ) );
330         if( typeNames.back()->empty() ) {
331             delete typeNames.back();
332             typeNames.pop_back();
333         } else {
334             SkipSimpleRecord( _file, buf, &err ); //exactly what does this do? if it doesn't count parenthesis, it probably should
335             buf.clear();
336         }
337         skipWS();
338         if( _file.peek() != ')' ) {
339             // do something
340         }
341     }
342     // STEPComplex needs an array of strings or of char*. construct the latter using c_str() on all strings in the vector
343     //FIXME: STEPComplex ctor should accept std::vector<std::string *> ?
344     const int s = typeNames.size();
345     const char ** names = new const char * [ s + 1 ];
346     names[ s ] = 0;
347     for( int i = 0; i < s; i++ ) {
348         names[ i ] = typeNames[i]->c_str();
349     }
350     //TODO still need the schema name
351     STEPcomplex * sc = new STEPcomplex( ( const_cast<Registry *>( reg ) ), names, ( int ) fileid /*, schnm*/ );
352     delete[] names;
353     //TODO also delete contents of typeNames!
354     return sc;
355 }
356 
357