1 /** \file STEPfile.cc
2  * NIST STEP Core Class Library
3  * cleditor/STEPfile.cc
4  * February, 1994
5  * Peter Carr
6  * K. C. Morris
7  * David Sauder
8 
9  * Development of this software was funded by the United States Government,
10  * and is not subject to copyright.
11 
12  TODO LIST:
13  - ReadHeader doesn't merge the information from more than one instance
14    of the same entity type name. i.e. it doesn't merge info from multiple files
15  - ReadWorkingFile does not handle references to entities which
16    aren't in the working session file. for other *incomplete* instances,
17    it prints error messages to cerr which may not need to be printed. It
18    doesn't keep track of the incomplete instances, it just puts them on the
19    list according to the symbol which precedes them in the working session
20    file
21 */
22 #include <iostream>
23 #include <iterator>
24 #include <algorithm>
25 #include <vector>
26 
27 #include <STEPfile.h>
28 #include <sdai.h>
29 #include <STEPcomplex.h>
30 #include <STEPattribute.h>
31 #include <SdaiHeaderSchema.h>
32 
33 // STEPundefined contains
34 // void PushPastString (istream& in, std::string &s, ErrorDescriptor *err)
35 #include <STEPundefined.h>
36 
37 #include "sc_memmgr.h"
38 
39 /**
40  * \returns The new file name for the class.
41  * \param newName The file name to be set.
42  *
43  * This function sets the _fileName member variable to an
44  * absolute path name to a directory on the file system. If newName
45  * cannot be resolved to a file, then an empty string is returned.
46  *
47  * side effects: STEPfile::_fileName value may change.
48  */
SetFileName(const std::string newName)49 std::string STEPfile::SetFileName( const std::string newName ) {
50     //  if a newName is not given or is the same as the old, use the old name
51     if( ( newName.empty() ) || ( newName == _fileName ) ) {
52         return FileName();
53     }
54 
55     _fileName = DirObj::Normalize( newName );
56     return _fileName;
57 }
58 
59 /** Returns read progress,scaled 0-100. Will return a value < 0 if
60  * called *immediately* after read operation starts, if no file has
61  * been specified, or if file has been closed. If result < 50,
62  * ReadData1() hasn't completed yet.
63  *
64  * This function is useless unless it is called from another thread.
65  */
GetReadProgress() const66 float STEPfile::GetReadProgress() const {
67     if( _iFileSize < 1 ) {
68         return -1;
69     }
70     //the file is read once by ReadData1(), and again by ReadData2. Each gets 50%.
71     float percent = ( static_cast<float>( _iFileCurrentPosition ) / _iFileSize ) * 50.0;
72     if( _iFileStage1Done ) {
73         percent += 50;
74     }
75     return percent;
76 }
77 
78 /** Returns write progress,scaled 0-100. Will return a value < 0 if
79  * called *immediately* after write operation starts, if no file has
80  * been specified, or if file has been closed.
81  * \sa GetReadProgress()
82  *
83  * Caveat: Only works for files written via WriteExchangeFile() / WriteData().
84  *
85  * This function is useless unless it is called from another thread.
86  */
GetWriteProgress() const87 float STEPfile::GetWriteProgress() const {
88     int total = _instances.InstanceCount();
89     if( total > 0 ) {
90         return ( static_cast<float>( _oFileInstsWritten ) / total ) * 100.0;
91     } else {
92         return -1;
93     }
94 }
95 
96 /**
97  * \param in The input stream from which the file is read.
98  *
99  * This function reads in the header section of an exchange file. It
100  * parses the header section, popluates the _headerInstances, and
101  * returns an error descriptor.
102  * It expects to find the "HEADER;" symbol at the beginning of the istream.
103  *
104  * side effects: The function  gobbles all characters up to and including the
105  * next "ENDSEC;" from in.
106  * The STEPfile::_headerInstances may change.
107  */
ReadHeader(istream & in)108 Severity STEPfile::ReadHeader( istream & in ) {
109     std::string cmtStr;
110 
111     InstMgr * im = new InstMgr;
112     SDAI_Application_instance * obj;
113     Severity objsev = SEVERITY_NULL;
114 
115     int endsec = 0;
116     int userDefined = 0;
117     int fileid;
118     std::string keywd;
119     char c = '\0';
120     char buf [BUFSIZ];
121 
122     std::string strbuf;
123 
124     ReadTokenSeparator( in );
125 
126     // Read and gobble all 'junk' up to "HEADER;"
127     if( !FindHeaderSection( in ) ) {
128         delete im;
129         return SEVERITY_INPUT_ERROR;
130     }
131 
132     //read the header instances
133     while( !endsec ) {
134         ReadTokenSeparator( in, &cmtStr );
135         if( in.eof() ) {
136             _error.AppendToDetailMsg( "End of file reached in reading header section.\n" );
137             _error.GreaterSeverity( SEVERITY_EXIT );
138             delete im;
139             return SEVERITY_EXIT;
140         }
141 
142         //check for user defined instances
143         //if it is userDefined, the '!' does not get put back on the istream
144         in.get( c );
145         if( c == '!' ) {
146             userDefined = 1;
147         } else          {
148             in.putback( c );
149         }
150 
151         //get the entity keyword
152         keywd = GetKeyword( in, ";( /\\", _error );
153         ReadTokenSeparator( in, &cmtStr );
154 
155         //check for "ENDSEC"
156         if( !strncmp( const_cast<char *>( keywd.c_str() ), "ENDSEC", 7 ) ) {
157             //get the token delimiter
158             in.get( c ); //should be ';'
159             endsec = 1;
160             break; //from while-loop
161         } else {
162             //create and read the header instance
163             // SDAI_Application_instance::STEPread now reads the opening parenthesis
164 
165             //create header instance
166             buf[0] = '\0';
167             if( _fileType == VERSION_OLD ) {
168                 _error.AppendToDetailMsg( "N279 header detected. Files this old are no longer supported.\n" );
169                 _error.GreaterSeverity( SEVERITY_EXIT );
170                 delete im;
171                 return SEVERITY_EXIT;
172             } else {
173                 strncpy( buf, const_cast<char *>( keywd.c_str() ), BUFSIZ );
174             }
175 
176             if( userDefined ) {
177                 //create user defined header instance
178                 // BUG: user defined entities are ignored
179                 //obj = _headerUserDefined->ObjCreate (buf);
180                 //objsev = AppendEntityErrorMsg( &(obj->Error()) );
181 
182                 SkipInstance( in, strbuf );
183                 cerr << "User defined entity in header section " <<
184                      "is ignored.\n\tdata lost: !" <<  buf << strbuf << "\n";
185                 _error.GreaterSeverity( SEVERITY_WARNING );
186                 break; //from while loop
187             } else { //not userDefined
188                 obj = _headerRegistry->ObjCreate( buf );
189             }
190 
191             //read header instance
192             if( !obj || ( obj == ENTITY_NULL ) ) {
193                 ++_errorCount;
194                 SkipInstance( in, strbuf );
195                 cerr << "Unable to create header section entity: \'" <<
196                      keywd << "\'.\n\tdata lost: " << strbuf << "\n";
197                 _error.GreaterSeverity( SEVERITY_WARNING );
198             } else { //not ENTITY_NULL
199                 //read the header instance
200 
201                 //check obj's Error Descriptor
202                 objsev = AppendEntityErrorMsg( &( obj->Error() ) );
203 
204                 //set file_id to reflect the appropriate Header Section Entity
205                 fileid = HeaderId( const_cast<char *>( keywd.c_str() ) );
206 
207                 //read the values from the istream
208                 objsev = obj->STEPread( fileid, 0, ( InstMgr * )0, in, NULL, true, _strict );
209                 if( !cmtStr.empty() ) {
210                     obj->PrependP21Comment( cmtStr );
211                 }
212 
213                 in >> ws;
214                 c = in.peek(); // check for semicolon or keyword 'ENDSEC'
215                 if( c != 'E' ) {
216                     in >> c;    // read the semicolon
217                 }
218 
219                 //check to see if object was successfully read
220                 AppendEntityErrorMsg( &( obj->Error() ) );
221                 //append to header instance manager
222                 im->Append( obj, completeSE );
223             }
224         }
225         cmtStr.clear();
226     }
227 
228     HeaderVerifyInstances( im );
229     HeaderMergeInstances( im ); // handles delete for im
230 
231     return _error.severity();
232 }
233 
234 /**
235  * Verify the instances read from the header section of an exchange file.
236  * If some required instances aren't present, then create them,
237  * and populate them with default values.
238  * The required instances are:
239  *  #1 = FILE_DESCRIPTION
240  *  #2 = FILE_NAME
241  *  #3 = FILE_SCHEMA
242  */
HeaderVerifyInstances(InstMgr * im)243 Severity STEPfile::HeaderVerifyInstances( InstMgr * im ) {
244     int err = 0;
245     int fileid;
246     SDAI_Application_instance * obj;
247 
248     //check File_Name
249     fileid = HeaderId( "File_Name" );
250     if( !( im->FindFileId( fileid ) ) ) {
251         ++err;
252         cerr << "FILE_NAME instance not found in header section\n";
253         // create a File_Name entity and assign default values
254         obj = HeaderDefaultFileName();
255         im->Append( obj, completeSE );
256     }
257 
258     //check File_Description
259     fileid = HeaderId( "File_Description" );
260     if( !( im->FindFileId( fileid ) ) ) {
261         ++err;
262         cerr << "FILE_DESCRIPTION instance not found in header section\n";
263         // create a File_Description entity and assign default values
264         obj = HeaderDefaultFileDescription();
265         im->Append( obj, completeSE );
266     }
267 
268     //check File_Schema
269     fileid = HeaderId( "File_Schema" );
270     if( !( im->FindFileId( fileid ) ) ) {
271         ++err;
272         cerr << "FILE_SCHEMA instance not found in header section\n";
273         // create a File_Schema entity and read in default values
274         obj = HeaderDefaultFileSchema();
275         im->Append( obj, completeSE );
276     }
277 
278     if( !err ) {
279         return SEVERITY_NULL;
280     }
281     _error.AppendToUserMsg( "Missing required entity in header section.\n" );
282     _error.GreaterSeverity( SEVERITY_WARNING );
283     return SEVERITY_WARNING;
284 }
285 
HeaderDefaultFileName()286 SDAI_Application_instance * STEPfile::HeaderDefaultFileName() {
287     SdaiFile_name * fn = new SdaiFile_name;
288     StringAggregate_ptr tmp = new StringAggregate;
289 
290     fn->name_( "" );
291     fn->time_stamp_( "" );
292     tmp->StrToVal( "", &_error, fn->attributes[2].getADesc()->DomainType(), _headerInstances );
293     fn->author_( tmp );
294 
295     tmp->StrToVal( "", &_error, fn->attributes[3].getADesc()->DomainType(), _headerInstances );
296     fn->organization_( tmp );
297 
298     fn->preprocessor_version_( "" );
299     fn->originating_system_( "" );
300     fn->authorization_( "" );
301 
302     fn->STEPfile_id = HeaderId( "File_Name" );
303 
304     return fn;
305 }
306 
HeaderDefaultFileDescription()307 SDAI_Application_instance * STEPfile::HeaderDefaultFileDescription() {
308     SdaiFile_description * fd = new SdaiFile_description;
309 
310     fd->implementation_level_( "" );
311 
312     fd->STEPfile_id = HeaderId( "File_Description" );
313 
314     return fd;
315 }
316 
HeaderDefaultFileSchema()317 SDAI_Application_instance * STEPfile::HeaderDefaultFileSchema() {
318     SdaiFile_schema * fs = new SdaiFile_schema;
319     StringAggregate_ptr tmp = new StringAggregate;
320 
321     tmp->StrToVal( "", &_error, fs->attributes[0].getADesc()->DomainType(), _headerInstances );
322     fs->schema_identifiers_( tmp );
323 
324     fs->STEPfile_id = HeaderId( "File_Schema" );
325 
326     return fs;
327 }
328 
329 
330 
331 /**
332  * This function has an effect when more than one file
333  * is being read into a working session.
334  *
335  * This function manages space allocation for the Instance
336  * Manager for the header instances. If the instances in im are
337  * copied onto the instances of _headerInstances, then im is
338  * deleted. Otherwise no space is deleted.
339  *
340  * Append the values of the given instance manager onto the _headerInstances.
341  * If the _headerInstances contain no instances, then copy the instances
342  * from im onto the _headerInstances.
343  * This only works for an instance manager which contains the following
344  * header section entites. The file id numbers are important.
345  *
346  * #1 = FILE_DESCRIPTION
347  * #2 = FILE_NAME
348  * #3 = FILE_SCHEMA
349  */
HeaderMergeInstances(InstMgr * im)350 void STEPfile::HeaderMergeInstances( InstMgr * im ) {
351     SDAI_Application_instance * se = 0;
352     SDAI_Application_instance * from = 0;
353 
354     int idnum;
355 
356     //check for _headerInstances
357     if( !_headerInstances ) {
358         _headerInstances = im;
359         return;
360     }
361 
362     if( _headerInstances->InstanceCount() < 4 ) {
363         delete _headerInstances;
364         _headerInstances = im;
365         return;
366     }
367 
368     //checking for _headerInstances::FILE_NAME
369     idnum = HeaderId( "File_Name" );
370     se = _headerInstances->GetApplication_instance( _headerInstances->FindFileId( idnum ) );
371     if( se ) {
372         from = im->GetApplication_instance( im->FindFileId( idnum ) );
373 
374         // name:
375         // time_stamp: keep the newer time_stamp
376         // author: append the list of authors
377         // organization: append the organization list
378         // preprocessor_version:
379         // originating_system:
380         // authorization:
381     } else { // No current File_Name instance
382         from = im->GetApplication_instance( im->FindFileId( idnum ) );
383         _headerInstances->Append( from, completeSE );
384     }
385 
386     //checking for _headerInstances::FILE_DESCRIPTION
387     idnum = HeaderId( "File_Description" );
388     se = _headerInstances->GetApplication_instance( _headerInstances->FindFileId( idnum ) );
389     if( se ) {
390         from = im->GetApplication_instance( im->FindFileId( idnum ) );
391 
392         //description
393         //implementation_level
394     } else {
395         from = im->GetApplication_instance( im->FindFileId( idnum ) );
396         _headerInstances->Append( from, completeSE );
397     }
398 
399     //checking for _headerInstances::FILE_SCHEMA
400     idnum = HeaderId( "File_Schema" );
401     se = _headerInstances->GetApplication_instance( _headerInstances->FindFileId( idnum ) );
402     if( se ) {
403         from = im->GetApplication_instance( im->FindFileId( idnum ) );
404 
405         //description
406         //implementation_level
407     } else {
408         from = im->GetApplication_instance( im->FindFileId( idnum ) );
409         _headerInstances->Append( from, completeSE );
410     }
411 
412     delete im;
413     return;
414 }
415 
416 
EntityWfState(char c)417 stateEnum STEPfile::EntityWfState( char c ) {
418     switch( c ) {
419         case wsSaveComplete:
420             return completeSE;
421         case wsSaveIncomplete:
422             return incompleteSE;
423         case wsDelete:
424             return deleteSE;
425         case wsNew:
426             return newSE;
427         default:
428             return noStateSE;
429     }
430 }
431 
432 /**
433  * PASS 1:  create instances
434  * starts at the data section
435  */
ReadData1(istream & in)436 int STEPfile::ReadData1( istream & in ) {
437     int endsec = 0;
438     _entsNotCreated = 0;
439 
440     _errorCount = 0;  // reset error count
441     _warningCount = 0;  // reset error count
442 
443     char c;
444     int instance_count = 0;
445     char buf[BUFSIZ];
446     buf[0] = '\0';
447     std::string tmpbuf;
448 
449     SDAI_Application_instance * obj = ENTITY_NULL;
450     stateEnum inst_state = noStateSE; // used if reading working file
451 
452     ErrorDescriptor e;
453 
454     //  PASS 1:  create instances
455     endsec = FoundEndSecKywd( in );
456     while( in.good() && !endsec ) {
457         e.ClearErrorMsg();
458         ReadTokenSeparator( in ); // also skips white space
459         in >> c;
460 
461         if( _fileType == WORKING_SESSION ) {
462             if( strchr( "CIND", c ) ) { // if there is a valid char
463                 inst_state = EntityWfState( c );
464                 ReadTokenSeparator( in );
465                 in >> c;    // read the ENTITY_NAME_DELIM
466             } else {
467                 e.AppendToDetailMsg( "Invalid editing state character: " );
468                 e.AppendToDetailMsg( c );
469                 e.AppendToDetailMsg( "\nAssigning editing state to be INCOMPLETE\n" );
470                 e.GreaterSeverity( SEVERITY_WARNING );
471                 inst_state = incompleteSE;
472             }
473         }
474 
475         if( c != ENTITY_NAME_DELIM ) {
476             in.putback( c );
477             while( c != ENTITY_NAME_DELIM && in.good() &&
478                     !( endsec = FoundEndSecKywd( in ) ) ) {
479                 tmpbuf.clear();
480                 FindStartOfInstance( in, tmpbuf );
481                 cout << "ERROR: trying to recover from invalid data. skipping: "
482                      << tmpbuf << endl;
483                 in >> c;
484                 ReadTokenSeparator( in );
485             }
486         }
487 
488         if( !endsec ) {
489             obj = ENTITY_NULL;
490             if( ( _fileType == WORKING_SESSION ) && ( inst_state == deleteSE ) ) {
491                 SkipInstance( in, tmpbuf );
492             } else {
493                 obj =  CreateInstance( in, cout );
494                 _iFileCurrentPosition = in.tellg();
495             }
496 
497             if( obj != ENTITY_NULL ) {
498                 if( obj->Error().severity() < SEVERITY_WARNING ) {
499                     ++_errorCount;
500                 } else if( obj->Error().severity() < SEVERITY_NULL ) {
501                     ++_warningCount;
502                 }
503                 obj->Error().ClearErrorMsg();
504 
505                 if( _fileType == WORKING_SESSION ) {
506                     instances().Append( obj, inst_state );
507                 } else {
508                     instances().Append( obj, newSE );
509                 }
510 
511                 ++instance_count;
512             } else {
513                 ++_entsNotCreated;
514                 //old
515                 ++_errorCount;
516             }
517 
518             if( _entsNotCreated > _maxErrorCount ) {
519                 _error.AppendToUserMsg( "Warning: Too Many Errors in File. Read function aborted.\n" );
520                 cerr << Error().UserMsg();
521                 cerr << Error().DetailMsg();
522                 Error().ClearErrorMsg();
523                 Error().severity( SEVERITY_EXIT );
524                 return instance_count;
525             }
526 
527             endsec = FoundEndSecKywd( in );
528 
529         }
530     } // end while loop
531 
532     if( _entsNotCreated ) {
533         sprintf( buf,
534                  "STEPfile Reading File: Unable to create %d instances.\n\tIn first pass through DATA section. Check for invalid entity types.\n",
535                  _entsNotCreated );
536         _error.AppendToUserMsg( buf );
537         _error.GreaterSeverity( SEVERITY_WARNING );
538     }
539     if( !in.good() ) {
540         _error.AppendToUserMsg( "Error in input file.\n" );
541     }
542 
543     _iFileStage1Done = true;
544     return instance_count;
545 }
546 
ReadWorkingData1(istream & in)547 int STEPfile::ReadWorkingData1( istream & in ) {
548     return ReadData1( in );
549 }
550 
551 /**
552  * \returns number of valid instances read
553  * reads in the data portion of the instances in an exchange file
554  */
ReadData2(istream & in,bool useTechCor)555 int STEPfile::ReadData2( istream & in, bool useTechCor ) {
556     _entsInvalid = 0;
557     _entsIncomplete = 0;
558     _entsWarning = 0;
559 
560     int total_instances = 0;
561     int valid_insts = 0;    // used for exchange file only
562     stateEnum inst_state = noStateSE; // used if reading working file
563 
564     _errorCount = 0;  // reset error count
565     _warningCount = 0;  // reset error count
566 
567     char c;
568     char buf[BUFSIZ];
569     buf[0] = '\0';
570     std::string tmpbuf;
571 
572     SDAI_Application_instance * obj = ENTITY_NULL;
573     std::string cmtStr;
574 
575     int endsec = FoundEndSecKywd( in );
576 
577     //  PASS 2:  read instances
578     while( in.good() && !endsec ) {
579         ReadTokenSeparator( in, &cmtStr );
580         in >> c;
581 
582         if( _fileType == WORKING_SESSION ) {
583             if( strchr( "CIND", c ) ) { // if there is a valid char
584                 inst_state = EntityWfState( c );
585                 ReadTokenSeparator( in, &cmtStr );
586                 in >> c;    // read the ENTITY_NAME_DELIM
587             }
588         }
589 
590         if( c != ENTITY_NAME_DELIM ) {
591             in.putback( c );
592             while( c != ENTITY_NAME_DELIM && in.good() &&
593                     !( endsec = FoundEndSecKywd( in ) ) ) {
594 
595                 tmpbuf.clear();
596                 FindStartOfInstance( in, tmpbuf );
597                 cout << "ERROR: trying to recover from invalid data. skipping: "
598                      << tmpbuf << endl;
599                 in >> c;
600                 ReadTokenSeparator( in, &cmtStr );
601             }
602         }
603 
604 
605         if( !endsec ) {
606             obj = ENTITY_NULL;
607             if( ( _fileType == WORKING_SESSION ) && ( inst_state == deleteSE ) ) {
608                 SkipInstance( in, tmpbuf );
609             } else {
610                 obj =  ReadInstance( in, cout, cmtStr, useTechCor );
611                 _iFileCurrentPosition = in.tellg();
612             }
613 
614             cmtStr.clear();
615             if( obj != ENTITY_NULL ) {
616                 if( obj->Error().severity() < SEVERITY_INCOMPLETE ) {
617                     ++_entsInvalid;
618                     // old
619                     ++_errorCount;
620                 } else if( obj->Error().severity() == SEVERITY_INCOMPLETE ) {
621                     ++_entsIncomplete;
622                     ++_entsInvalid;
623                 } else if( obj->Error().severity() == SEVERITY_USERMSG ) {
624                     ++_entsWarning;
625                 } else { // i.e. if severity == SEVERITY_NULL
626                     ++valid_insts;
627                 }
628 
629                 obj->Error().ClearErrorMsg();
630 
631                 ++total_instances;
632             } else {
633                 ++_entsInvalid;
634                 // old
635                 ++_errorCount;
636             }
637 
638             if( _entsInvalid > _maxErrorCount ) {
639                 _error.AppendToUserMsg( "Warning: Too Many Errors in File. Read function aborted.\n" );
640                 cerr << Error().UserMsg();
641                 cerr << Error().DetailMsg();
642                 Error().ClearErrorMsg();
643                 Error().severity( SEVERITY_EXIT );
644                 return valid_insts;
645             }
646 
647             endsec = FoundEndSecKywd( in );
648         }
649     } // end while loop
650 
651     if( _entsInvalid ) {
652         sprintf( buf,
653                  "%s \n\tTotal instances: %d \n\tInvalid instances: %d \n\tIncomplete instances (includes invalid instances): %d \n\t%s: %d.\n",
654                  "Second pass complete - instance summary:", total_instances,
655                  _entsInvalid, _entsIncomplete, "Warnings",
656                  _entsWarning );
657         cout << buf << endl;
658         _error.AppendToUserMsg( buf );
659         _error.AppendToDetailMsg( buf );
660         _error.GreaterSeverity( SEVERITY_WARNING );
661     }
662     if( !in.good() ) {
663         _error.AppendToUserMsg( "Error in input file.\n" );
664     }
665 
666     return valid_insts;
667 }
668 
ReadWorkingData2(istream & in,bool useTechCor)669 int STEPfile::ReadWorkingData2( istream & in, bool useTechCor ) {
670     return ReadData2( in, useTechCor );
671 }
672 
673 /** Looks for the word DATA followed by optional whitespace
674  * followed by a semicolon.  When it is looking for the word
675  * DATA it skips over strings and comments.
676  */
FindDataSection(istream & in)677 int STEPfile::FindDataSection( istream & in ) {
678     ErrorDescriptor errs;
679     SDAI_String tmp;
680     std::string s; // used if need to read a comment
681     char c;
682 
683     while( in.good() ) {
684         in >> c;
685         if( in.eof() ) {
686             _error.AppendToUserMsg( "Can't find \"DATA;\" section." );
687             return 0; //ERROR_WARNING
688         }
689         switch( c ) {
690             case 'D':  //  look for string "DATA;" with optional whitespace after "DATA"
691                 c = in.peek(); // look at next char (for 'A')
692                 // only peek since it may be 'D' again a we need to start over
693                 if( c == 'A' ) {
694                     in.get( c ); // read char 'A'
695                     c = in.peek();  // look for 'T'
696                     if( c == 'T' ) {
697                         in.get( c );    // read 'T'
698                         c = in.peek();  // look for 'A'
699                         if( c == 'A' ) {
700                             in.get( c ); // read 'A'
701                             in >> ws; // may want to skip comments or print control directives?
702                             c = in.peek();  // look for semicolon
703                             if( c == ';' ) {
704                                 in.get( c ); // read the semicolon
705                                 return 1; // success
706                             }
707                         }
708                     }
709                 }
710                 break;
711 
712             case '\'':  // get past the string
713                 in.putback( c );
714                 tmp.STEPread( in, &errs );
715                 break;
716 
717             case '/': // read p21 file comment
718                 in.putback( c ); // looks like a comment
719                 ReadComment( in, s );
720                 break;
721 
722             case '\0':  // problem in input ?
723                 return 0;
724 
725             default:
726                 break;
727         }
728     }
729     return 0;
730 }
731 
FindHeaderSection(istream & in)732 int STEPfile::FindHeaderSection( istream & in ) {
733     char buf[BUFSIZ];
734     char * b = buf;
735 
736     *b = '\0';
737 
738     ReadTokenSeparator( in );
739     //  find the header section
740     while( !( b = strstr( buf, "HEADER" ) ) ) {
741         if( in.eof() ) {
742             _error.AppendToUserMsg(
743                 "Error: Unable to find HEADER section. File not read.\n" );
744             _error.GreaterSeverity( SEVERITY_INPUT_ERROR );
745             return 0;
746         }
747         in.getline( buf, BUFSIZ, ';' ); // reads but does not store the ;
748     }
749     return 1;
750 }
751 //dasdelete
752 
753 //dasdelete
754 /**
755  description:
756     This function creates an instance based on the KEYWORD
757     read from the istream.
758     It expects an ENTITY_NAME (int) from the first set of
759     characters on the istream. If the (int) is not found,
760     ENTITY_NULL is returned.
761 
762   side effects:
763     The function leaves the istream set to the end of the stream
764     of characters representing the ENTITY_INSTANCE. It attempts to
765     recover on errors by reading up to and including the next ';'.
766 
767 an ENTITY_INSTANCE consists of:
768    '#'(int)'=' [SCOPE] SIMPLE_RECORD ';' ||
769    '#'(int)'=' [SCOPE] SUBSUPER_RECORD ';'
770 The '#' is read from the istream before CreateInstance is called.
771  */
CreateInstance(istream & in,ostream & out)772 SDAI_Application_instance * STEPfile::CreateInstance( istream & in, ostream & out ) {
773     std::string tmpbuf;
774     std::string objnm;
775 
776     char c;
777     std::string schnm;
778 
779     int fileid = -1;
780     SDAI_Application_instance_ptr * scopelist = 0;
781 
782     SDAI_Application_instance * obj;
783     ErrorDescriptor result;
784     // Sent down to CreateSubSuperInstance() to receive error info
785 
786     ReadTokenSeparator( in );
787 
788     in >> fileid; // read instance id
789     fileid = IncrementFileId( fileid );
790     if( instances().FindFileId( fileid ) ) {
791         SkipInstance( in, tmpbuf );
792         out <<  "ERROR: instance #" << fileid
793             << " already exists.\n\tData lost: " << tmpbuf << endl;
794         return ENTITY_NULL;
795     }
796 
797     ReadTokenSeparator( in );
798     in.get( c ); // read equal sign
799     if( c != '=' ) {
800         // ERROR: '=' expected
801         SkipInstance( in, tmpbuf );
802         out << "ERROR: instance #" << fileid
803             << " \'=\' expected.\n\tData lost: " << tmpbuf << endl;
804         return ENTITY_NULL;
805     }
806 
807     ReadTokenSeparator( in );
808     c = in.peek(); // peek at the next character on the istream
809 
810     //check for optional "&SCOPE" construct
811     if( c == '&' ) { // TODO check this out
812         Severity s = CreateScopeInstances( in, &scopelist );
813         if( s < SEVERITY_WARNING ) {
814             return ENTITY_NULL;
815         }
816         ReadTokenSeparator( in );
817         c = in.peek(); // peek at next char on istream again
818     }
819 
820     //check for subtype/supertype record
821     if( c == '(' ) {
822         //  TODO:  implement complex inheritance
823 
824         obj = CreateSubSuperInstance( in, fileid, result );
825         if( obj == ENTITY_NULL ) {
826             SkipInstance( in, tmpbuf );
827             out << "ERROR: instance #" << fileid
828                 << " Illegal complex entity.\n"
829                 << result.UserMsg() << ".\n\n";
830             return ENTITY_NULL;
831         }
832     } else { // not a complex entity
833         // check for User Defined Entity
834         int userDefined = 0;
835         if( c == '!' ) {
836             userDefined = 1;
837             in.get( c );
838         }
839 
840         ReadStdKeyword( in, objnm, 1 ); // read the type name
841         if( !in.good() ) {
842             out << "ERROR: instance #" << fileid
843                 << " Unexpected file problem in "
844                 << "STEPfile::CreateInstance.\n";
845         }
846 
847         //create the instance using the Registry object
848         if( userDefined ) {
849             SkipInstance( in, tmpbuf );
850             out << "WARNING: instance #" << fileid
851                 << " User Defined Entity in DATA section ignored.\n"
852                 << "\tData lost: \'!" << objnm << "\': " << tmpbuf
853                 << endl;
854             return ENTITY_NULL;
855         } else {
856             schnm = schemaName();
857             obj = reg().ObjCreate( objnm.c_str(), schnm.c_str() );
858             if( obj == ENTITY_NULL ) {
859                 // This will be the case if objnm does not exist in the reg.
860                 result.UserMsg( "Unknown ENTITY type" );
861             } else if( obj->Error().severity() <= SEVERITY_WARNING ) {
862                 // Common causes of error is that obj is an abstract supertype
863                 // or that it can only be instantiated using external mapping.
864                 // If neither are the case, create a generic message.
865                 if( !obj->Error().UserMsg().empty() ) {
866                     result.UserMsg( obj->Error().UserMsg() );
867                 } else {
868                     result.UserMsg( "Could not create ENTITY" );
869                 }
870                 // Delete obj so that below we'll know that an error occur:
871                 delete obj;
872                 obj = ENTITY_NULL;
873             }
874         }
875     }
876 
877     if( obj == ENTITY_NULL ) {
878         SkipInstance( in, tmpbuf );
879         out << "ERROR: instance #" << fileid << " \'" << objnm
880             << "\': " << result.UserMsg()
881             << ".\n\tData lost: " << tmpbuf << "\n\n";
882         return ENTITY_NULL;
883     }
884     obj -> STEPfile_id = fileid;
885 
886     //  scan values
887     SkipInstance( in, tmpbuf );
888 
889     ReadTokenSeparator( in );
890     return obj;
891 }
892 
893 
894 /**
895  description:
896     This function reads the SCOPE list for an entity instance,
897     creates each instance on the scope list, and appending each
898     instance to the instance manager.
899  side-effects:
900     It first searches for "&SCOPE" and reads all characters
901     from the istream up to and including "ENDSCOPE"
902  returns: ErrorDescriptor
903     >= SEVERITY_WARNING: the istream was read up to and including the
904                       "ENDSCOPE" and the export list /#1, ... #N/
905     <  SEVERITY_WARNING: the istream was read up to and including the next ";"
906     < SEVERITY_BUG: fatal
907  */
CreateScopeInstances(istream & in,SDAI_Application_instance_ptr ** scopelist)908 Severity STEPfile::CreateScopeInstances( istream & in, SDAI_Application_instance_ptr ** scopelist ) {
909     Severity rval = SEVERITY_NULL;
910     SDAI_Application_instance * se;
911     std::string tmpbuf;
912     char c;
913     std::vector< SDAI_Application_instance_ptr > inscope;
914     std::string keywd;
915 
916     keywd = GetKeyword( in, " \n\t/\\#;", _error );
917     if( strncmp( const_cast<char *>( keywd.c_str() ), "&SCOPE", 6 ) ) {
918         //ERROR: "&SCOPE" expected
919         //TODO: should attempt to recover by reading through ENDSCOPE
920         //currently recovery is attempted by reading through next ";"
921         SkipInstance( in, tmpbuf );
922         cerr << "ERROR: " << "\'&SCOPE\' expected." <<
923              "\n\tdata lost: " << tmpbuf << "\n";
924         return SEVERITY_INPUT_ERROR;
925     }
926 
927     ReadTokenSeparator( in );
928 
929     in.get( c );
930     while( c == '#' ) {
931         se = CreateInstance( in, cout );
932         if( se != ENTITY_NULL ) {
933             //TODO:  apply scope information to se
934             //  Add se to scopelist
935             inscope.push_back( se );
936 
937             //append the se to the instance manager
938             instances().Append( se, newSE );
939         } else {
940             //ERROR: instance in SCOPE not created
941             rval = SEVERITY_WARNING;
942             SkipInstance( in, tmpbuf );
943             cerr << "instance in SCOPE not created.\n\tdata lost: "
944                  << tmpbuf << "\n";
945             ++_errorCount;
946         }
947 
948         ReadTokenSeparator( in );
949         in.get( c );
950     }
951     in.putback( c );
952     *scopelist = new SDAI_Application_instance_ptr [inscope.size()];
953     for( size_t i = 0; i < inscope.size(); ++i ) {
954         *scopelist[i] = inscope[i];
955     }
956 
957     //check for "ENDSCOPE"
958     keywd = GetKeyword( in, " \t\n/\\#;", _error );
959     if( strncmp( const_cast<char *>( keywd.c_str() ), "ENDSCOPE", 8 ) ) {
960         //ERROR: "ENDSCOPE" expected
961         SkipInstance( in, tmpbuf );
962         cerr << "ERROR: " << "\'ENDSCOPE\' expected."
963              << "\n\tdata lost: " << tmpbuf << "\n";
964         ++_errorCount;
965         return SEVERITY_INPUT_ERROR;
966     }
967 
968     //check for export list
969     ReadTokenSeparator( in );
970     in.get( c );
971     in.putback( c );
972     if( c == '/' ) {
973         //read export list
974         in.get( c );
975         c = ',';
976         while( c == ',' ) {
977             int exportid;
978             ReadTokenSeparator( in );
979             in.get( c );
980             if( c != '#' )  {  } //ERROR
981             in >> exportid;
982             //TODO: nothing is done with the idnums on the export list
983             ReadTokenSeparator( in );
984             in.get( c );
985         }
986         if( c != '/' ) {
987             //ERROR: '/' expected while reading export list
988             SkipInstance( in, tmpbuf );
989             cerr << "ERROR:  \'/\' expected in export list.\n\tdata lost: " << tmpbuf << "\n";
990             ++_errorCount;
991             rval =  SEVERITY_INPUT_ERROR;
992         }
993         ReadTokenSeparator( in );
994     }
995     return rval;
996 }
997 
CreateSubSuperInstance(istream & in,int fileid,ErrorDescriptor & e)998 SDAI_Application_instance * STEPfile::CreateSubSuperInstance( istream & in, int fileid, ErrorDescriptor & e ) {
999     SDAI_Application_instance * obj = ENTITY_NULL;
1000 
1001     char c;
1002     std::string schnm;
1003 
1004     std::string buf; // used to hold the simple record that is read
1005     ErrorDescriptor err; // used to catch error msgs
1006 
1007     const int enaSize = 64;
1008     std::string * entNmArr[enaSize]; // array of entity type names
1009     int enaIndex = 0;
1010 
1011     in >> ws;
1012     in.get( c ); // read the open paren
1013     c = in.peek(); // see if you have closed paren (ending the record)
1014     while( in.good() && ( c != ')' ) && ( enaIndex < enaSize ) ) {
1015         entNmArr[enaIndex] = new std::string( "" );
1016         ReadStdKeyword( in, *( entNmArr[enaIndex] ), 1 ); // read the type name
1017         if( entNmArr[enaIndex]->empty() ) {
1018             delete entNmArr[enaIndex];
1019             entNmArr[enaIndex] = 0;
1020         } else {
1021             SkipSimpleRecord( in, buf, &err );
1022             buf.clear();
1023             enaIndex++;
1024         }
1025         in >> ws;
1026         c = in.peek(); // see if you have closed paren (ending the record)
1027         // If someone separates the entities with commas (or some other
1028         // garbage or a comment) this will keep the read function from
1029         // infinite looping.  If the entity name starts with a digit it is
1030         // incorrect and will be invalid.
1031         while( in.good() && ( c != ')' ) && !isalpha( c ) ) {
1032             in >> c; // skip the invalid char
1033             c = in.peek(); // see if you have closed paren (ending the record)
1034         }
1035     }
1036     entNmArr[enaIndex] = 0;
1037     schnm = schemaName();
1038 
1039     obj = new STEPcomplex( &_reg, ( const std::string ** )entNmArr, fileid, schnm.c_str() );
1040 
1041     if( obj->Error().severity() <= SEVERITY_WARNING ) {
1042         // If obj is not legal, record its error info and delete it:
1043         e.severity( obj->Error().severity() );
1044         e.UserMsg( obj->Error().UserMsg() );
1045         delete obj;
1046         obj = ENTITY_NULL;
1047     }
1048 
1049     enaIndex = 0;
1050     while( entNmArr[enaIndex] != 0 ) {
1051         delete entNmArr[enaIndex];
1052         enaIndex ++;
1053     }
1054 
1055     return obj;
1056 }
1057 
1058 /**
1059  description:
1060     This function reads the SCOPE list for an entity instance,
1061     and reads the values for each instance from the istream.
1062  side-effects:
1063     It first searches for "&SCOPE" and reads all characters
1064     from the istream up to and including "ENDSCOPE"
1065  */
ReadScopeInstances(istream & in)1066 Severity STEPfile::ReadScopeInstances( istream & in ) {
1067     Severity rval = SEVERITY_NULL;
1068     SDAI_Application_instance * se;
1069     std::string tmpbuf;
1070     char c;
1071     std::string keywd;
1072     std::string cmtStr;
1073 
1074     keywd = GetKeyword( in, " \n\t/\\#;", _error );
1075     if( strncmp( const_cast<char *>( keywd.c_str() ), "&SCOPE", 6 ) ) {
1076         //ERROR: "&SCOPE" expected
1077         SkipInstance( in, tmpbuf );
1078         cerr << "\'&SCOPE\' expected.\n\tdata lost: " << tmpbuf << "\n";
1079         ++_errorCount;
1080         return SEVERITY_WARNING;
1081     }
1082 
1083     ReadTokenSeparator( in );
1084 
1085     in.get( c );
1086     while( c == '#' ) {
1087         se = ReadInstance( in, cout, cmtStr );
1088         if( se != ENTITY_NULL ) {
1089             //apply scope information to se
1090             //TODO: not yet implemented
1091         } else {
1092             //ERROR: unable to read instance in SCOPE
1093             rval = SEVERITY_WARNING;
1094         }
1095 
1096         ReadTokenSeparator( in );
1097         in.get( c );
1098     }
1099     in.putback( c );
1100 
1101     //check for "ENDSCOPE"
1102     keywd = GetKeyword( in, " \t\n/\\#;", _error );
1103     if( strncmp( const_cast<char *>( keywd.c_str() ), "ENDSCOPE", 8 ) ) {
1104         //ERROR: "ENDSCOPE" expected
1105         SkipInstance( in, tmpbuf );
1106         cerr << " \'ENDSCOPE\' expected.\n\tdata lost: " << tmpbuf << "\n";
1107         ++_errorCount;
1108         return SEVERITY_WARNING;
1109     }
1110 
1111     //check for export list
1112     ReadTokenSeparator( in );
1113     in.get( c );
1114     in.putback( c );
1115     if( c == '/' ) {
1116         //read through export list
1117         in.get( c );
1118         c = ',';
1119         while( c == ',' ) {
1120             int exportid;
1121             ReadTokenSeparator( in );
1122             in.get( c );
1123             in >> exportid;
1124             ReadTokenSeparator( in );
1125             in.get( c );
1126         }
1127         if( c != '/' ) {
1128             //ERROR: '/' expected while reading export list
1129             SkipInstance( in, tmpbuf );
1130             cerr << " \'/\' expected while reading export list.\n\tdata lost: "
1131                  << tmpbuf << "\n";
1132             ++_errorCount;
1133             rval =  SEVERITY_WARNING;
1134         }
1135         ReadTokenSeparator( in );
1136     }
1137     return rval;
1138 }
1139 
1140 /**
1141  This function populates a SDAI_Application_instance with the values read from
1142  the istream.
1143 
1144  This function must keeps track of error messages encountered when
1145  reading the SDAI_Application_instance. It passes SDAI_Application_instance error information onto
1146  the STEPfile ErrorDescriptor.
1147 */
ReadInstance(istream & in,ostream & out,std::string & cmtStr,bool useTechCor)1148 SDAI_Application_instance * STEPfile::ReadInstance( istream & in, ostream & out, std::string & cmtStr,
1149         bool useTechCor ) {
1150     Severity sev = SEVERITY_NULL;
1151 
1152     std::string tmpbuf;
1153     char errbuf[BUFSIZ];
1154     errbuf[0] = '\0';
1155     std::string currSch;
1156     std::string objnm;
1157 
1158     char c;
1159     int fileid;
1160     SDAI_Application_instance * obj = ENTITY_NULL;
1161     int idIncrNum = FileIdIncr();
1162 
1163     ReadComment( in, cmtStr );
1164 
1165     in >> fileid;
1166     fileid = IncrementFileId( fileid );
1167 
1168     //  check to see that instance was created on PASS 1
1169     MgrNode * node = instances().FindFileId( fileid );
1170     if( ( !node ) || ( ( obj =  node -> GetApplication_instance() ) == ENTITY_NULL ) ) {
1171         SkipInstance( in, tmpbuf );
1172         // Changed the 2nd pass error message to report Part 21 User
1173         // Defined Entities. STEPfile still includes them in the error count
1174         // which is not valid.
1175         // Check to see if an User Defined Entity has been found.
1176         const char * ude = tmpbuf.c_str();
1177         while( *ude && ( *ude != '=' ) ) {
1178             ude++;
1179         }
1180         if( *ude == '=' ) {
1181             ude++;
1182         }
1183         while( *ude && isspace( *ude ) ) {
1184             ude++;
1185         }
1186         if( *ude == '!' ) {
1187             out << "\nWARNING: #" << fileid <<
1188                 " - Ignoring User Defined Entity.\n\tData lost: "
1189                 << tmpbuf << endl;
1190         } else
1191             out << "\nERROR: in 2nd pass, instance #" << fileid
1192                 << " not found.\n\tData lost: " << tmpbuf << endl;
1193         return ENTITY_NULL;
1194     } else if( ( _fileType != WORKING_SESSION ) && ( node->CurrState() != newSE ) ) {
1195         SkipInstance( in, tmpbuf );
1196         out << "\nERROR: in 2nd pass, instance #" << fileid
1197             << " already exists - ignoring duplicate.\n\tData lost: "
1198             << tmpbuf << endl;
1199         return ENTITY_NULL;
1200     }
1201 
1202     ReadTokenSeparator( in, &cmtStr );
1203 
1204     in.get( c );
1205     if( c != '=' ) {
1206         //ERROR: '=' expected
1207         SkipInstance( in, tmpbuf );
1208         out << "ERROR: instance #" << fileid
1209             << " \'=\' expected.\n\tData lost: " << tmpbuf << endl;
1210         return ENTITY_NULL;
1211     }
1212 
1213     ReadTokenSeparator( in, &cmtStr );
1214 
1215     //peek at the next character on the istream
1216     c = in.peek();
1217 
1218     //check for optional "&SCOPE" construct
1219     if( c == '&' ) {
1220         ReadScopeInstances( in );
1221         ReadTokenSeparator( in, &cmtStr );
1222         in.get( c );
1223         in.putback( c );
1224     }
1225 
1226     currSch = schemaName();
1227 
1228     //check for subtype/supertype record
1229     if( c == '(' ) {
1230         // TODO
1231         sev = obj->STEPread( fileid, idIncrNum, &instances(), in, currSch.c_str(),
1232                              useTechCor, _strict );
1233 
1234         ReadTokenSeparator( in, &cmtStr );
1235 
1236         if( !cmtStr.empty() ) {
1237             obj->AddP21Comment( cmtStr );
1238         }
1239 
1240         c = in.peek(); // check for semicolon or keyword 'ENDSEC'
1241         if( c != 'E' ) {
1242             in >> c;    // read the semicolon
1243         }
1244     } else {
1245         ReadTokenSeparator( in, &cmtStr );
1246         c = in.peek();
1247 
1248         // check for User Defined Entity
1249         // DAS - I checked this out. It doesn't get into this code for user
1250         // defined entities because a ude isn't created.
1251         int userDefined = 0;
1252         if( c == '!' ) {
1253             userDefined = 1;
1254             in.get( c );
1255         }
1256 
1257         ReadStdKeyword( in, objnm, 1 ); // read the type name
1258         if( !in.good() ) {
1259             out << "ERROR: instance #" << fileid
1260                 << " Unexpected file problem in "
1261                 << "STEPfile::ReadInstance." << endl;
1262         }
1263         ReadTokenSeparator( in, &cmtStr );
1264 
1265         //  read values
1266         if( userDefined ) {
1267             SkipInstance( in, tmpbuf );
1268             out << "WARNING: #" << fileid <<
1269                 ". Ignoring User defined entity." << endl << "    data lost: !"
1270                 << objnm << tmpbuf << endl;
1271             ++_warningCount;
1272             return ENTITY_NULL;
1273         }
1274 
1275         // NOTE: this function is called for all FileTypes
1276         // (WORKING_SESSION included)
1277 
1278         sev = obj->STEPread( fileid, idIncrNum, &instances(), in, currSch.c_str(),
1279                              useTechCor, _strict );
1280 
1281         ReadTokenSeparator( in, &cmtStr );
1282 
1283         if( !cmtStr.empty() ) {
1284             obj->AddP21Comment( cmtStr );
1285         }
1286 
1287         c = in.peek(); // check for semicolon or keyword 'ENDSEC'
1288         if( c != 'E' ) {
1289             in >> c;    // read the semicolon
1290         }
1291 
1292         AppendEntityErrorMsg( &( obj->Error() ) );
1293     }
1294 
1295     //set the node's state,
1296     //and set the STEPfile:_error (based on the type of file being read)
1297     switch( sev ) {
1298         case SEVERITY_NULL:
1299         case SEVERITY_USERMSG:
1300             if( _fileType != WORKING_SESSION ) {
1301                 node->ChangeState( completeSE );
1302             }
1303             break;
1304 
1305         case SEVERITY_WARNING:
1306         case SEVERITY_INPUT_ERROR:
1307         case SEVERITY_BUG:
1308 
1309         case SEVERITY_INCOMPLETE:
1310             if( ( _fileType == VERSION_CURRENT ) ) {
1311                 cerr << "ERROR in EXCHANGE FILE: incomplete instance #"
1312                      << obj -> STEPfile_id << ".\n";
1313                 if( _fileType != WORKING_SESSION ) {
1314                     node->ChangeState( incompleteSE );
1315                 }
1316             } else {
1317                 if( node->CurrState() == completeSE ) {
1318                     sprintf( errbuf, "WARNING in WORKING FILE: changing instance #%d state from completeSE to incompleteSE.\n", fileid );
1319                     _error.AppendToUserMsg( errbuf );
1320                     if( _fileType != WORKING_SESSION ) {
1321                         node->ChangeState( incompleteSE );
1322                     }
1323                 }
1324             }
1325             break;
1326 
1327         case SEVERITY_EXIT:
1328         case SEVERITY_DUMP:
1329         case SEVERITY_MAX:
1330             if( _fileType != WORKING_SESSION ) {
1331                 node->ChangeState( noStateSE );
1332             }
1333             break;
1334 
1335         default:
1336             break;
1337     }
1338 
1339     // check ErrorDesc severity and set the state for MgrNode *node
1340     // according to completeSE or incompleteSE
1341     // watch how you set it based on whether you are reading an
1342     // exchange or working file.
1343 
1344     return obj;
1345 
1346 }
1347 
1348 
1349 
1350 /**
1351 This function uses the C library function system to issue
1352 a shell command which checks for the existence of the
1353 file name and creates a backup file if the file exists.
1354 The system command takes a string, which must be a valid
1355 sh command. The command is:
1356    if (test -f filename) then mv filename filename.bak; fi
1357 
1358 BUG: doesn't check to see if the backup command works.
1359      the results of the system call are not used by the
1360      by this function
1361 */
MakeBackupFile()1362 void STEPfile::MakeBackupFile() {
1363     std::string bckup = FileName();
1364     bckup.append( ".bak" );
1365 
1366     std::fstream f( FileName().c_str(), std::fstream::in | std::fstream::binary );
1367     f << std::noskipws;
1368     std::istream_iterator<unsigned char> begin( f );
1369     std::istream_iterator<unsigned char> end;
1370 
1371     std::fstream f2( bckup.c_str(), std::fstream::out | std::fstream::trunc | std::fstream::binary );
1372     std::ostream_iterator<char> begin2( f2 );
1373 
1374     copy( begin, end, begin2 );
1375 
1376     _error.AppendToDetailMsg( "Making backup file: " );
1377     _error.AppendToDetailMsg( bckup.c_str() );
1378     _error.AppendToDetailMsg( "\n" );
1379 }
1380 
WriteExchangeFile(ostream & out,int validate,int clearError,int writeComments)1381 Severity STEPfile::WriteExchangeFile( ostream & out, int validate, int clearError,
1382                                       int writeComments ) {
1383     Severity rval = SEVERITY_NULL;
1384     SetFileType( VERSION_CURRENT );
1385     if( clearError ) {
1386         _error.ClearErrorMsg();
1387     }
1388 
1389     if( validate ) {
1390         rval = instances().VerifyInstances( _error );
1391         _error.GreaterSeverity( rval );
1392         if( rval < SEVERITY_USERMSG ) {
1393             _error.AppendToUserMsg( "Unable to verify instances. File not written. Try saving as working session file." );
1394             _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1395             return rval;
1396         }
1397     }
1398 
1399     out << FILE_DELIM << "\n";
1400     WriteHeader( out );
1401     WriteData( out, writeComments );
1402     out << END_FILE_DELIM << "\n";
1403     return rval;
1404 }
1405 
WriteExchangeFile(const std::string filename,int validate,int clearError,int writeComments)1406 Severity STEPfile::WriteExchangeFile( const std::string filename, int validate, int clearError,
1407                                       int writeComments ) {
1408     Severity rval = SEVERITY_NULL;
1409 
1410     if( clearError ) {
1411         _error.ClearErrorMsg();
1412     }
1413 
1414     if( validate ) {
1415         rval = instances().VerifyInstances( _error );
1416         _error.GreaterSeverity( rval );
1417         if( rval < SEVERITY_USERMSG ) {
1418             _error.AppendToUserMsg( "Unable to verify instances. File wasn't opened. Try saving as working session file.\n" );
1419             _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1420             return rval;
1421         }
1422     }
1423 
1424     ostream * out =  OpenOutputFile( filename );
1425     if( _error.severity() < SEVERITY_WARNING ) {
1426         return _error.severity();
1427     }
1428     rval = WriteExchangeFile( *out, 0, 0, writeComments );
1429     CloseOutputFile( out );
1430     return rval;
1431 }
1432 
WriteValuePairsFile(ostream & out,int validate,int clearError,int writeComments,int mixedCase)1433 Severity STEPfile::WriteValuePairsFile( ostream & out, int validate, int clearError,
1434                                         int writeComments, int mixedCase ) {
1435     Severity rval = SEVERITY_NULL;
1436     SetFileType( VERSION_CURRENT );
1437     if( clearError ) {
1438         _error.ClearErrorMsg();
1439     }
1440 
1441     if( validate ) {
1442         rval = instances().VerifyInstances( _error );
1443         _error.GreaterSeverity( rval );
1444         if( rval < SEVERITY_USERMSG ) {
1445             _error.AppendToUserMsg( "Unable to verify instances. File not written. Try saving as working session file." );
1446             _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1447             return rval;
1448         }
1449     }
1450 
1451     WriteValuePairsData( out, writeComments, mixedCase );
1452     return rval;
1453 }
1454 
1455 /**
1456 This function returns an integer value for
1457 the file id for a header section entity,
1458 based on a given entity name.
1459 
1460 This function may change the value of the
1461 STEPfile member variable: _headerId
1462 
1463 The header section entities must be numbered in the following manner:
1464 
1465 #1=FILE_DESCRIPTION
1466 #2=FILE_NAME
1467 #3=FILE_SCHEMA
1468 */
HeaderId(const char * name)1469 int STEPfile::HeaderId( const char * name ) {
1470     std::string tmp = name;
1471 
1472     std::transform( tmp.begin(), tmp.end(), tmp.begin(), ::toupper );
1473 
1474     if( tmp == "FILE_DESCRIPTION" ) {
1475         return 1;
1476     }
1477     if( tmp == "FILE_NAME" ) {
1478         return 2;
1479     }
1480     if( tmp == "FILE_SCHEMA" ) {
1481         return 3;
1482     }
1483     return ++_headerId;
1484 }
1485 
1486 /***************************
1487 ***************************/
WriteHeader(ostream & out)1488 void STEPfile::WriteHeader( ostream & out ) {
1489     out << "HEADER;\n";
1490 
1491     WriteHeaderInstanceFileDescription( out );
1492     WriteHeaderInstanceFileName( out );
1493     WriteHeaderInstanceFileSchema( out );
1494 
1495     // Write the rest of the header instances
1496     SDAI_Application_instance * se;
1497     int n = _headerInstances->InstanceCount();
1498     for( int i = 0; i < n; ++i ) {
1499         se = _headerInstances->GetMgrNode( i ) ->GetApplication_instance();
1500         if( !(
1501                     ( se->StepFileId() == HeaderId( "File_Name" ) ) ||
1502                     ( se->StepFileId() == HeaderId( "File_Description" ) ) ||
1503                     ( se->StepFileId() == HeaderId( "File_Schema" ) )
1504                 ) )
1505             WriteHeaderInstance(
1506                 _headerInstances->GetMgrNode( i )->GetApplication_instance(), out );
1507     }
1508     out << "ENDSEC;\n";
1509 }
1510 
1511 /***************************
1512 ***************************/
WriteHeaderInstance(SDAI_Application_instance * obj,ostream & out)1513 void STEPfile::WriteHeaderInstance( SDAI_Application_instance * obj, ostream & out ) {
1514     std::string tmp;
1515     if( !obj->P21Comment().empty() ) {
1516         out << obj->P21Comment();
1517     }
1518     out << StrToUpper( obj->EntityName(), tmp ) << "(";
1519     int n = obj->attributes.list_length();
1520     for( int i = 0; i < n; ++i ) {
1521         ( obj->attributes[i] ).STEPwrite( out );
1522         if( i < n - 1 ) {
1523             out << ",";
1524         }
1525     }
1526     out << ");\n";
1527 }
1528 
1529 /***************************
1530 ***************************/
WriteHeaderInstanceFileName(ostream & out)1531 void STEPfile::WriteHeaderInstanceFileName( ostream & out ) {
1532 // Get the FileName instance from _headerInstances
1533     SDAI_Application_instance * se = 0;
1534     se = _headerInstances->GetApplication_instance( "File_Name" );
1535     if( se == ENTITY_NULL ) {
1536         se = ( SDAI_Application_instance * )HeaderDefaultFileName();
1537     }
1538 
1539 //set some of the attribute values at time of output
1540     SdaiFile_name * fn = ( SdaiFile_name * )se;
1541 
1542     /* I'm not sure this is a good idea that Peter did but I'll leave around - DAS
1543         // write time_stamp (as specified in ISO Standard 8601)
1544         // output the current system time to the file, using the following format:
1545         // example: '1994-04-12T15:27:46'
1546         // for Calendar Date, 12 April 1994, 27 minute 46 seconds past 15 hours
1547     */
1548     time_t t = time( NULL );
1549     struct tm * timeptr = localtime( &t );
1550     char time_buf[26];
1551     strftime( time_buf, 26, "'%Y-%m-%dT%H:%M:%S'", timeptr );
1552     fn->time_stamp_( time_buf );
1553 
1554 //output the values to the file
1555     WriteHeaderInstance( se, out );
1556 }
1557 
WriteHeaderInstanceFileDescription(ostream & out)1558 void STEPfile::WriteHeaderInstanceFileDescription( ostream & out ) {
1559 // Get the FileDescription instance from _headerInstances
1560     SDAI_Application_instance * se = 0;
1561     se = _headerInstances->GetApplication_instance( "File_Description" );
1562     if( se == ENTITY_NULL ) {
1563         // ERROR: no File_Name instance in _headerInstances
1564         // create a File_Name instance
1565         se = ( SDAI_Application_instance * )HeaderDefaultFileDescription();
1566     }
1567     WriteHeaderInstance( se, out );
1568 }
1569 
WriteHeaderInstanceFileSchema(ostream & out)1570 void STEPfile::WriteHeaderInstanceFileSchema( ostream & out ) {
1571 // Get the FileName instance from _headerInstances
1572     SDAI_Application_instance * se = 0;
1573     se = _headerInstances->GetApplication_instance( "File_Schema" );
1574     if( se == ENTITY_NULL ) {
1575         // ERROR: no File_Name instance in _headerInstances
1576         // create a File_Name instance
1577         se = ( SDAI_Application_instance * ) HeaderDefaultFileSchema();
1578     }
1579     WriteHeaderInstance( se, out );
1580 }
1581 
WriteData(ostream & out,int writeComments)1582 void STEPfile::WriteData( ostream & out, int writeComments ) {
1583     _oFileInstsWritten = 0;
1584     std::string currSch = schemaName();
1585     out << "DATA;\n";
1586 
1587     int n = instances().InstanceCount();
1588     for( int i = 0; i < n; ++i ) {
1589         instances().GetMgrNode( i )->GetApplication_instance()->STEPwrite( out, currSch.c_str(), writeComments );
1590         _oFileInstsWritten++;
1591     }
1592 
1593     out << "ENDSEC;\n";
1594 }
1595 
WriteValuePairsData(ostream & out,int writeComments,int mixedCase)1596 void STEPfile::WriteValuePairsData( ostream & out, int writeComments, int mixedCase ) {
1597     std::string currSch = schemaName();
1598     int n = instances().InstanceCount();
1599     for( int i = 0; i < n; ++i ) {
1600         instances().GetMgrNode( i )->GetApplication_instance()->WriteValuePairs( out, currSch.c_str(), writeComments, mixedCase );
1601     }
1602 }
1603 
AppendFile(istream * in,bool useTechCor)1604 Severity STEPfile::AppendFile( istream * in, bool useTechCor ) {
1605     Severity rval = SEVERITY_NULL;
1606     char errbuf[BUFSIZ];
1607 
1608     SetFileIdIncrement();
1609     int total_insts = 0,  valid_insts = 0;
1610 
1611     ReadTokenSeparator( *in );
1612     std::string keywd = GetKeyword( *in, "; #", _error );
1613     // get the delimiter off the istream
1614     char c;
1615     in->get( c );
1616 
1617     if( !strncmp( const_cast<char *>( keywd.c_str() ), "ISO-10303-21",
1618                   strlen( const_cast<char *>( keywd.c_str() ) ) ) ) {
1619         SetFileType( VERSION_CURRENT );
1620     } else if( !strncmp( const_cast<char *>( keywd.c_str() ), "STEP_WORKING_SESSION",
1621                          strlen( const_cast<char *>( keywd.c_str() ) ) ) ) {
1622         if( _fileType != WORKING_SESSION ) {
1623             _error.AppendToUserMsg(
1624                 "Warning: Reading in file as Working Session file.\n" );
1625             _error.GreaterSeverity( SEVERITY_WARNING );
1626         }
1627         SetFileType( WORKING_SESSION );
1628     } else {
1629         sprintf( errbuf,
1630                  "Faulty input at beginning of file. \"ISO-10303-21;\" or"
1631                  " \"STEP_WORKING_SESSION;\" expected. File not read: %s\n",
1632                  ( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) );
1633         _error.AppendToUserMsg( errbuf );
1634         _error.GreaterSeverity( SEVERITY_INPUT_ERROR );
1635         return SEVERITY_INPUT_ERROR;
1636     }
1637 
1638     cout << "Reading Data from " << ( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) << "...\n";
1639 
1640     //  Read header
1641     rval = ReadHeader( *in );
1642     cout << "\nHEADER read:";
1643     if( rval < SEVERITY_WARNING ) {
1644         sprintf( errbuf,
1645                  "Error: non-recoverable error in reading header section. "
1646                  "There were %d errors encountered. Rest of file is ignored.\n",
1647                  _errorCount );
1648         _error.AppendToUserMsg( errbuf );
1649         return rval;
1650     } else if( rval != SEVERITY_NULL ) {
1651         sprintf( errbuf, "  %d  ERRORS\t  %d  WARNINGS\n\n",
1652                  _errorCount, _warningCount );
1653         cout << errbuf;
1654     } else {
1655         cout << endl;
1656     }
1657 
1658     if( !FindDataSection( *in ) ) {
1659         _error.AppendToUserMsg( "Error: Unable to find DATA section delimiter. Data section not read. Rest of file ignored.\n" );
1660         return SEVERITY_INPUT_ERROR;
1661     }
1662 
1663     //  PASS 1
1664     _errorCount = 0;
1665     total_insts = ReadData1( *in );
1666 
1667     cout << "\nFIRST PASS complete:  " << total_insts
1668          << " instances created.\n";
1669     sprintf( errbuf,
1670              "  %d  ERRORS\t  %d  WARNINGS\n\n",
1671              _errorCount, _warningCount );
1672     cout << errbuf;
1673 
1674     //  PASS 2
1675     //  This would be nicer if you didn't actually have to close the
1676     //  file but could just reposition the pointer back to the
1677     //  beginning of the data section.  It looks like you can do this
1678     //  with the GNU File class, but that class doesn't have the
1679     //  operator >> overloaded which is used to do the rest of the
1680     //  parsing.  SO we are using istreams and this works, but could
1681     //  be better.
1682 
1683     // reset the error count so you're not counting things twice:
1684     _errorCount = 0;
1685     istream * in2;
1686     if( !( ( in2 = OpenInputFile() ) && ( in2 -> good() ) ) ) {
1687         //  if the stream is not readable, there's an error
1688         _error.AppendToUserMsg( "Cannot open file for 2nd pass -- No data read.\n" );
1689         CloseInputFile( in2 );
1690         return SEVERITY_INPUT_ERROR;
1691     }
1692     if( !FindDataSection( *in2 ) ) {
1693         _error.AppendToUserMsg( "Error: Unable to find DATA section delimiter in second pass. \nData section not read. Rest of file ignored.\n" );
1694         CloseInputFile( in2 );
1695         return  SEVERITY_INPUT_ERROR;
1696     }
1697 
1698     switch( _fileType ) {
1699         case VERSION_CURRENT:
1700         case VERSION_UNKNOWN:
1701         case WORKING_SESSION:
1702             valid_insts = ReadData2( *in2, useTechCor );
1703             break;
1704         default:
1705             _error.AppendToUserMsg( "STEPfile::AppendFile: STEP file version set to unrecognized value.\n" );
1706             CloseInputFile( in2 );
1707             return  SEVERITY_BUG;
1708     }
1709 
1710     //check for "ENDSEC;"
1711     ReadTokenSeparator( *in2 );
1712     if( total_insts != valid_insts ) {
1713         sprintf( errbuf, "%d invalid instances in file: %s\n",
1714                  total_insts - valid_insts, ( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) );
1715         _error.AppendToUserMsg( errbuf );
1716         CloseInputFile( in2 );
1717         return _error.GreaterSeverity( SEVERITY_WARNING );
1718     }
1719 
1720     cout << "\nSECOND PASS complete:  " << valid_insts
1721          << " instances valid.\n";
1722     sprintf( errbuf,
1723              "  %d  ERRORS\t  %d  WARNINGS\n\n",
1724              _errorCount, _warningCount );
1725     _error.AppendToUserMsg( errbuf );
1726     cout << errbuf;
1727 
1728 
1729     //check for "ENDSTEP;" || "END-ISO-10303-21;"
1730 
1731     if( in2 -> good() ) {
1732         ReadTokenSeparator( *in2 );
1733         keywd = GetKeyword( *in2, ";", _error );
1734         //yank the ";" from the istream
1735         //if (';' == in2->peek()) in2->get();
1736         char ch;
1737         in2->get( ch );
1738         if( ch != ';' ) {
1739             std::cerr << __FILE__ << ":" << __LINE__ << " - Expected ';' at Part 21 EOF, found '" << c << "'." << std::endl;
1740         }
1741     }
1742 
1743     if( ( !keywd.compare( 0, keywd.size(), END_FILE_DELIM ) ) || !( in2 -> good() ) ) {
1744         _error.AppendToUserMsg( END_FILE_DELIM );
1745         _error.AppendToUserMsg( " missing at end of file.\n" );
1746         CloseInputFile( in2 );
1747         return _error.GreaterSeverity( SEVERITY_WARNING );
1748     }
1749     CloseInputFile( in2 );
1750     cout << "Finished reading file.\n\n";
1751     return SEVERITY_NULL;
1752 }
1753 
WriteWorkingFile(ostream & out,int clearError,int writeComments)1754 Severity STEPfile::WriteWorkingFile( ostream & out, int clearError, int writeComments ) {
1755     SetFileType( WORKING_SESSION );
1756     if( clearError ) {
1757         _error.ClearErrorMsg();
1758     }
1759 
1760     if( instances().VerifyInstances( _error ) < SEVERITY_INCOMPLETE ) {
1761         _error.AppendToUserMsg( "WARNING: some invalid instances written to working session file. Data may have been lost." );
1762         _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1763     }
1764 
1765     out << FILE_DELIM << "\n";
1766     WriteHeader( out );
1767 
1768     WriteWorkingData( out, writeComments );
1769     out << END_FILE_DELIM << "\n";
1770     SetFileType();
1771 
1772     return _error.severity();
1773 }
1774 
WriteWorkingFile(const std::string filename,int clearError,int writeComments)1775 Severity STEPfile::WriteWorkingFile( const std::string filename, int clearError,
1776                                      int writeComments ) {
1777     if( clearError ) {
1778         _error.ClearErrorMsg();
1779     }
1780     ostream * out =  OpenOutputFile( filename );
1781     if( _error.severity() < SEVERITY_WARNING ) {
1782         return _error.severity();
1783     }
1784     Severity rval = WriteWorkingFile( *out, 0, writeComments );
1785     CloseOutputFile( out );
1786 
1787     return rval;
1788 }
1789 
WriteWorkingData(ostream & out,int writeComments)1790 void STEPfile::WriteWorkingData( ostream & out, int writeComments ) {
1791     std::string currSch = schemaName();
1792     out << "DATA;\n";
1793     int n = instances().InstanceCount();
1794 
1795     for( int i = 0; i < n; ++i ) {
1796         switch( instances().GetMgrNode( i )->CurrState() ) {
1797             case deleteSE:
1798                 out << wsDelete;
1799                 instances().GetMgrNode( i )->GetApplication_instance()->
1800                 STEPwrite( out, currSch.c_str(), writeComments );
1801                 break;
1802             case completeSE:
1803                 out << wsSaveComplete;
1804                 instances().GetMgrNode( i )->GetApplication_instance()->
1805                 STEPwrite( out, currSch.c_str(), writeComments );
1806                 break;
1807             case incompleteSE:
1808                 out << wsSaveIncomplete;
1809                 instances().GetMgrNode( i )->GetApplication_instance()->
1810                 STEPwrite( out, currSch.c_str(), writeComments );
1811                 break;
1812             case newSE:
1813                 out << wsNew;
1814                 instances().GetMgrNode( i )->GetApplication_instance()->
1815                 STEPwrite( out, currSch.c_str(), writeComments );
1816                 break;
1817             case noStateSE:
1818                 _error.AppendToUserMsg( "no state information for this node\n" );
1819                 break;
1820         }
1821     }
1822     out << "ENDSEC;\n";
1823 }
1824 
1825 /**
1826     This function sends messages to 'cerr'
1827     based on the values in the given ErrorDescriptor (e), which is
1828     supposed to be from a SDAI_Application_instance object, being manipulated by one
1829     of the STEPfile member functions.
1830 
1831     The given error descriptor's messages are then cleared.
1832 
1833     The STEPfile's error descriptor is set no lower than SEVERITY_WARNING.
1834 
1835 */
AppendEntityErrorMsg(ErrorDescriptor * e)1836 Severity STEPfile::AppendEntityErrorMsg( ErrorDescriptor * e ) {
1837     ErrorDescriptor * ed = e;
1838 
1839     Severity sev = ed->severity();
1840 
1841     if( ( sev < SEVERITY_MAX ) || ( sev > SEVERITY_NULL ) ) {
1842         //ERROR: something wrong with ErrorDescriptor
1843         _error.GreaterSeverity( SEVERITY_WARNING );
1844         return SEVERITY_BUG;
1845     }
1846 
1847     switch( sev ) {
1848         case SEVERITY_NULL:
1849             return SEVERITY_NULL;
1850 
1851         default: {
1852             cerr << e->DetailMsg();
1853             e->ClearErrorMsg();
1854 
1855             if( sev < SEVERITY_USERMSG )   {
1856                 ++_errorCount;
1857             }
1858             if( sev < SEVERITY_WARNING )   {
1859                 sev = SEVERITY_WARNING;
1860             }
1861 
1862             _error.GreaterSeverity( sev );
1863             return sev;
1864         }
1865     }
1866 }
1867