1
2 #include <errordesc.h>
3 #include <stdio.h>
4 #include <sdai.h>
5 #include <read_func.h>
6 #include <STEPattribute.h>
7 #include "Str.h"
8 #include "sc_memmgr.h"
9
10 const int RealNumPrecision = REAL_NUM_PRECISION;
11
12 // print Error information for debugging purposes
13 void
PrintErrorState(ErrorDescriptor & err)14 PrintErrorState( ErrorDescriptor & err ) {
15 cout << "** severity: ";
16 switch( err.severity() ) {
17 case SEVERITY_NULL :
18 cout << "\n Null\n";
19 break;
20 case SEVERITY_USERMSG :
21 cout << "\n User Message\n";
22 break;
23 case SEVERITY_INCOMPLETE :
24 cout << "\n Incomplete\n";
25 break;
26 case SEVERITY_WARNING :
27 cout << "\n Warning\n";
28 break;
29 case SEVERITY_INPUT_ERROR :
30 cout << "\n Input Error\n";
31 break;
32 default:
33 cout << "\n Other\n";
34 break;
35 }
36 cout << err.DetailMsg() << "\n";
37 }
38
39 // print istream error information for debugging purposes
IStreamState(istream & in)40 void IStreamState( istream & in ) {
41 if( in.good() ) {
42 cerr << "istream GOOD\n" << flush;
43 }
44 if( in.fail() ) {
45 cerr << "istream FAIL\n" << flush;
46 }
47 if( in.eof() ) {
48 cerr << "istream EOF\n" << flush;
49 }
50 }
51
52 ///////////////////////////////////////////////////////////////////////////////
53 // ReadInteger
54 // * This function reads an integer if possible
55 // * If an integer is read it is assigned to val and 1 (true) is returned.
56 // * If an integer is not read because of an error then val is left unchanged
57 // and 0 (false) is returned.
58 // * If there is an error then the ErrorDescriptor err is set accordingly with
59 // a severity level and error message (no error MESSAGE is set for severity
60 // incomplete).
61 // * tokenList contains characters that terminate reading the value.
62 // * If tokenList is not zero then the istream will be read until a character
63 // is found matching a character in tokenlist. All values read up to the
64 // terminating character (delimiter) must be valid or err will be set with an
65 // appropriate error message. A valid value may still have been assigned
66 // but it may be followed by garbage thus an error will result. White
67 // space between the value and the terminating character is not considered
68 // to be invalid. If tokenList is null then the value must not be followed
69 // by any characters other than white space (i.e. EOF must happen)
70 //
71 ///////////////////////////////////////////////////////////////////////////////
ReadInteger(SDAI_Integer & val,istream & in,ErrorDescriptor * err,const char * tokenList)72 int ReadInteger( SDAI_Integer & val, istream & in, ErrorDescriptor * err,
73 const char * tokenList ) {
74 SDAI_Integer i = 0;
75 in >> ws;
76 in >> i;
77
78 int valAssigned = 0;
79
80 if( !in.fail() ) {
81 valAssigned = 1;
82 val = i;
83 }
84 CheckRemainingInput( in, err, "Integer", tokenList );
85 return valAssigned;
86 }
87
88 /// same as above but reads from a const char *
ReadInteger(SDAI_Integer & val,const char * s,ErrorDescriptor * err,const char * tokenList)89 int ReadInteger( SDAI_Integer & val, const char * s, ErrorDescriptor * err,
90 const char * tokenList ) {
91 istringstream in( ( char * )s );
92 return ReadInteger( val, in, err, tokenList );
93 }
94
95 ///////////////////////////////////////////////////////////////////////////////
96 // * attrValue is validated.
97 // * err is set if there is an error
98 // * If optional is 1 then a missing value will be valid otherwise severity
99 // incomplete will be set.
100 // * If you don\'t know if the value may be optional then set it false and
101 // check to see if SEVERITY_INCOMPLETE is set. You can change it later to
102 // SEVERITY_NULL if it is valid for the value to be missing. No error
103 // 'message' will be associated with the value being missing so you won\'t
104 // have to worry about undoing an error message.
105 // * tokenList contains characters that terminate the expected value.
106 // * If tokenList is not zero then the value is expected to terminate before
107 // a character found in tokenlist. All values read up to the
108 // terminating character (delimiter) must be valid or err will be set with an
109 // appropriate error message. White space between the value and the
110 // terminating character is not considered to be invalid. If tokenList is
111 // null then attrValue must only contain a valid value and nothing else
112 // following.
113 ///////////////////////////////////////////////////////////////////////////////
IntValidLevel(const char * attrValue,ErrorDescriptor * err,int clearError,int optional,const char * tokenList)114 Severity IntValidLevel( const char * attrValue, ErrorDescriptor * err,
115 int clearError, int optional, const char * tokenList ) {
116 if( clearError ) {
117 err->ClearErrorMsg();
118 }
119
120 istringstream in( ( char * )attrValue );
121 in >> ws; // skip white space
122 char c = in.peek();
123 if( in.eof() ) {
124 if( !optional ) {
125 err->GreaterSeverity( SEVERITY_INCOMPLETE );
126 }
127 } else if( c == '$' ) {
128 if( !optional ) {
129 err->GreaterSeverity( SEVERITY_INCOMPLETE );
130 }
131 in >> c;
132 CheckRemainingInput( in, err, "integer", tokenList );
133 return err->severity();
134 } else {
135 SDAI_Integer val = 0;
136 int valAssigned = ReadInteger( val, in, err, tokenList );
137 if( !valAssigned && !optional ) {
138 err->GreaterSeverity( SEVERITY_INCOMPLETE );
139 }
140 }
141 return err->severity();
142 }
143
WriteReal(SDAI_Real val)144 std::string WriteReal( SDAI_Real val ) {
145 char rbuf[64];
146 std::string s;
147
148 // out << form("%.*G", (int) Real_Num_Precision,tmp);
149 // replace the above line with this code so that writing the '.' is
150 // guaranteed for reals. If you use e or E then you get many
151 // unnecessary trailing zeros. g and G truncates all trailing zeros
152 // to save space but when no non-zero precision exists it also
153 // truncates the decimal. The decimal is required by Part 21.
154 // Also use G instead of g since G writes uppercase E (E instead of e
155 // is also required by Part 21) when scientific notation is used - DAS
156
157 sprintf( rbuf, "%.*G", ( int ) RealNumPrecision, val );
158 if( !strchr( rbuf, '.' ) ) {
159 if( strchr( rbuf, 'E' ) || strchr( rbuf, 'e' ) ) {
160 char * expon = strchr( rbuf, 'E' );
161
162 if( !expon ) {
163 expon = strchr( rbuf, 'e' );
164 }
165 *expon = '\0';
166 s = rbuf;
167 s.append( "." );
168 s.append( "E" );
169 expon++;
170 s += expon;
171 } else {
172 int rindex = strlen( rbuf );
173 rbuf[rindex] = '.';
174 rbuf[rindex + 1] = '\0';
175 s = rbuf;
176 }
177 } else {
178 s = rbuf;
179 }
180 return s;
181 }
182
WriteReal(SDAI_Real val,ostream & out)183 void WriteReal( SDAI_Real val, ostream & out ) {
184 out << WriteReal( val );
185 }
186
187 ///////////////////////////////////////////////////////////////////////////////
188 // ReadReal
189 // * This function reads a real if possible
190 // * If a real is read it is assigned to val and 1 (true) is returned.
191 // * If a real is not read because of an error then val is left unchanged
192 // and 0 (false) is returned.
193 // * If there is an error then the ErrorDescriptor err is set accordingly with
194 // a severity level and error message (no error MESSAGE is set for severity
195 // incomplete).
196 // * tokenList contains characters that terminate reading the value.
197 // * If tokenList is not zero then the istream will be read until a character
198 // is found matching a character in tokenlist. All values read up to the
199 // terminating character (delimiter) must be valid or err will be set with an
200 // appropriate error message. A valid value may still have been assigned
201 // but it may be followed by garbage thus an error will result. White
202 // space between the value and the terminating character is not considered
203 // to be invalid. If tokenList is null then the value must not be followed
204 // by any characters other than white space (i.e. EOF must happen)
205 //
206 // skip any leading whitespace characters
207 // read: optional sign, at least one decimal digit, required decimal point,
208 // zero or more decimal digits, optional letter e or E (but lower case e is
209 // an error), optional sign, at least one decimal digit if there is an E.
210 //
211 ///////////////////////////////////////////////////////////////////////////////
ReadReal(SDAI_Real & val,istream & in,ErrorDescriptor * err,const char * tokenList)212 int ReadReal( SDAI_Real & val, istream & in, ErrorDescriptor * err,
213 const char * tokenList ) {
214 SDAI_Real d = 0;
215
216 // Read the real's value into a string so we can make sure it is properly
217 // formatted. e.g. a decimal point is present. If you use the stream to
218 // read the real, it won't complain if the decimal place is missing.
219 char buf[64];
220 int i = 0;
221 char c;
222 ErrorDescriptor e;
223
224 in >> ws; // skip white space
225
226 // read optional sign
227 c = in.peek();
228 if( c == '+' || c == '-' ) {
229 in.get( buf[i++] );
230 c = in.peek();
231 }
232
233 // check for required initial decimal digit
234 if( !isdigit( c ) ) {
235 e.severity( SEVERITY_WARNING );
236 e.DetailMsg( "Real must have an initial digit.\n" );
237 }
238 // read one or more decimal digits
239 while( isdigit( c ) ) {
240 in.get( buf[i++] );
241 c = in.peek();
242 }
243
244 // read Part 21 required decimal point
245 if( c == '.' ) {
246 in.get( buf[i++] );
247 c = in.peek();
248 } else {
249 // It may be the number they wanted but it is incompletely specified
250 // without a decimal and thus it is an error
251 e.GreaterSeverity( SEVERITY_WARNING );
252 e.AppendToDetailMsg( "Reals are required to have a decimal point.\n" );
253 }
254
255 // read optional decimal digits
256 while( isdigit( c ) ) {
257 in.get( buf[i++] );
258 c = in.peek();
259 }
260
261 // try to read an optional E for scientific notation
262 if( ( c == 'e' ) || ( c == 'E' ) ) {
263 if( c == 'e' ) {
264 // this is incorrectly specified and thus is an error
265 e.GreaterSeverity( SEVERITY_WARNING );
266 e.AppendToDetailMsg(
267 "Reals using scientific notation must use upper case E.\n" );
268 }
269 in.get( buf[i++] ); // read the E
270 c = in.peek();
271
272 // read optional sign
273 if( c == '+' || c == '-' ) {
274 in.get( buf[i++] );
275 c = in.peek();
276 }
277
278 // read required decimal digit (since it has an E)
279 if( !isdigit( c ) ) {
280 e.GreaterSeverity( SEVERITY_WARNING );
281 e.AppendToDetailMsg(
282 "Real must have at least one digit following E for scientific notation.\n" );
283 }
284 // read one or more decimal digits
285 while( isdigit( c ) ) {
286 in.get( buf[i++] );
287 c = in.peek();
288 }
289 }
290 buf[i] = '\0';
291
292 istringstream in2( ( char * )buf );
293
294 // now that we have the real the stream will be able to salvage reading
295 // whatever kind of format was used to represent the real.
296 in2 >> d;
297
298 int valAssigned = 0;
299
300 if( !in2.fail() ) {
301 valAssigned = 1;
302 val = d;
303 err->GreaterSeverity( e.severity() );
304 err->AppendToDetailMsg( e.DetailMsg() );
305 } else {
306 val = S_REAL_NULL;
307 }
308
309 CheckRemainingInput( in, err, "Real", tokenList );
310 return valAssigned;
311 }
312
313 /// same as above but reads from a const char *
ReadReal(SDAI_Real & val,const char * s,ErrorDescriptor * err,const char * tokenList)314 int ReadReal( SDAI_Real & val, const char * s, ErrorDescriptor * err,
315 const char * tokenList ) {
316 istringstream in( ( char * )s );
317 return ReadReal( val, in, err, tokenList );
318 }
319
320 ///////////////////////////////////////////////////////////////////////////////
321 // * attrValue is validated.
322 // * err is set if there is an error
323 // * If optional is 1 then a missing value will be valid otherwise severity
324 // incomplete will be set.
325 // * If you don\'t know if the value may be optional then set it false and
326 // check to see if SEVERITY_INCOMPLETE is set. You can change it later to
327 // SEVERITY_NULL if it is valid for the value to be missing. No error
328 // 'message' will be associated with the value being missing so you won\'t
329 // have to worry about undoing an error message.
330 // * tokenList contains characters that terminate the expected value.
331 // * If tokenList is not zero then the value is expected to terminate before
332 // a character found in tokenlist. All values read up to the
333 // terminating character (delimiter) must be valid or err will be set with an
334 // appropriate error message. White space between the value and the
335 // terminating character is not considered to be invalid. If tokenList is
336 // null then attrValue must only contain a valid value and nothing else
337 // following.
338 ///////////////////////////////////////////////////////////////////////////////
RealValidLevel(const char * attrValue,ErrorDescriptor * err,int clearError,int optional,const char * tokenList)339 Severity RealValidLevel( const char * attrValue, ErrorDescriptor * err,
340 int clearError, int optional, const char * tokenList ) {
341 if( clearError ) {
342 err->ClearErrorMsg();
343 }
344
345 istringstream in( ( char * )attrValue );
346 in >> ws; // skip white space
347 char c = in.peek();
348 if( in.eof() ) {
349 if( !optional ) {
350 err->GreaterSeverity( SEVERITY_INCOMPLETE );
351 }
352 } else if( c == '$' ) {
353 if( !optional ) {
354 err->GreaterSeverity( SEVERITY_INCOMPLETE );
355 }
356 in >> c;
357 CheckRemainingInput( in, err, "real", tokenList );
358 return err->severity();
359 } else {
360 SDAI_Real val = 0;
361 int valAssigned = ReadReal( val, in, err, tokenList );
362 if( !valAssigned && !optional ) {
363 err->GreaterSeverity( SEVERITY_INCOMPLETE );
364 }
365 }
366 return err->severity();
367 }
368
369 /**
370 * ReadNumber - read as a real number
371 * * This function reads a number if possible
372 * * If a number is read it is assigned to val and 1 (true) is returned.
373 * * If a number is not read because of an error then val is left unchanged
374 * and 0 (false) is returned.
375 * * If there is an error then the ErrorDescriptor err is set accordingly with
376 * a severity level and error message (no error MESSAGE is set for severity
377 * incomplete).
378 * * tokenList contains characters that terminate reading the value.
379 * * If tokenList is not zero then the istream will be read until a character
380 * is found matching a character in tokenlist. All values read up to the
381 * terminating character (delimiter) must be valid or err will be set with an
382 * appropriate error message. A valid value may still have been assigned
383 * but it may be followed by garbage thus an error will result. White
384 * space between the value and the terminating character is not considered
385 * to be invalid. If tokenList is null then the value must not be followed
386 * by any characters other than white space (i.e. EOF must happen)
387 */
ReadNumber(SDAI_Real & val,istream & in,ErrorDescriptor * err,const char * tokenList)388 int ReadNumber( SDAI_Real & val, istream & in, ErrorDescriptor * err,
389 const char * tokenList ) {
390 SDAI_Real d = 0;
391 in >> ws;
392 in >> d;
393
394 int valAssigned = 0;
395 if( !in.fail() ) {
396 valAssigned = 1;
397 val = d;
398 }
399 CheckRemainingInput( in, err, "Number", tokenList );
400 return valAssigned;
401 }
402
403 /// same as above but reads from a const char *
ReadNumber(SDAI_Real & val,const char * s,ErrorDescriptor * err,const char * tokenList)404 int ReadNumber( SDAI_Real & val, const char * s, ErrorDescriptor * err,
405 const char * tokenList ) {
406 istringstream in( ( char * )s );
407 return ReadNumber( val, in, err, tokenList );
408 }
409
410
411 ///////////////////////////////////////////////////////////////////////////////
412 // * attrValue is validated.
413 // * err is set if there is an error
414 // * If optional is 1 then a missing value will be valid otherwise severity
415 // incomplete will be set.
416 // * If you don\'t know if the value may be optional then set it false and
417 // check to see if SEVERITY_INCOMPLETE is set. You can change it later to
418 // SEVERITY_NULL if it is valid for the value to be missing. No error
419 // 'message' will be associated with the value being missing so you won\'t
420 // have to worry about undoing an error message.
421 // * tokenList contains characters that terminate the expected value.
422 // * If tokenList is not zero then the value is expected to terminate before
423 // a character found in tokenlist. All values read up to the
424 // terminating character (delimiter) must be valid or err will be set with an
425 // appropriate error message. White space between the value and the
426 // terminating character is not considered to be invalid. If tokenList is
427 // null then attrValue must only contain a valid value and nothing else
428 // following.
429 ///////////////////////////////////////////////////////////////////////////////
NumberValidLevel(const char * attrValue,ErrorDescriptor * err,int clearError,int optional,const char * tokenList)430 Severity NumberValidLevel( const char * attrValue, ErrorDescriptor * err,
431 int clearError, int optional, const char * tokenList ) {
432 if( clearError ) {
433 err->ClearErrorMsg();
434 }
435
436 istringstream in( ( char * )attrValue );
437 in >> ws; // skip white space
438 char c = in.peek();
439 if( in.eof() ) {
440 if( !optional ) {
441 err->GreaterSeverity( SEVERITY_INCOMPLETE );
442 }
443 } else if( c == '$' ) {
444 if( !optional ) {
445 err->GreaterSeverity( SEVERITY_INCOMPLETE );
446 }
447 in >> c;
448 CheckRemainingInput( in, err, "number", tokenList );
449 return err->severity();
450 } else {
451 SDAI_Real val = 0;
452 int valAssigned = ReadNumber( val, in, err, tokenList );
453 if( !valAssigned && !optional ) {
454 err->GreaterSeverity( SEVERITY_INCOMPLETE );
455 }
456 }
457 return err->severity();
458 }
459
460 /// assign 's' so that it contains an exchange file format string read from 'in'.
PushPastString(istream & in,std::string & s,ErrorDescriptor * err)461 void PushPastString( istream & in, std::string & s, ErrorDescriptor * err ) {
462 s += GetLiteralStr( in, err );
463 }
464
465 /**
466 * assign 's' so that it contains an exchange file format aggregate read from 'in'.
467 * This is used to read aggregates that are part of multidimensional aggregates.
468 */
PushPastImbedAggr(istream & in,std::string & s,ErrorDescriptor * err)469 void PushPastImbedAggr( istream & in, std::string & s, ErrorDescriptor * err ) {
470 char messageBuf[BUFSIZ];
471 messageBuf[0] = '\0';
472
473 char c;
474 in >> ws;
475 in.get( c );
476
477 if( c == '(' ) {
478 s += c;
479 in.get( c );
480 while( in.good() && ( c != ')' ) ) {
481 if( c == '(' ) {
482 in.putback( c );
483 PushPastImbedAggr( in, s, err );
484 } else if( c == STRING_DELIM ) {
485 in.putback( c );
486 PushPastString( in, s, err );
487 } else {
488 s += c;
489 }
490 in.get( c );
491 }
492 if( c != ')' ) {
493 err->GreaterSeverity( SEVERITY_INPUT_ERROR );
494 sprintf( messageBuf, "Invalid aggregate value.\n" );
495 err->AppendToDetailMsg( messageBuf );
496 s.append( ")" );
497 } else {
498 s += c;
499 }
500 }
501 }
502
503 /**
504 * assign 's' so that it contains an exchange file format aggregate read from 'in'.
505 * This is used to read a single dimensional aggregate (i.e. it is not allowed
506 * to contain an aggregate as an element.
507 */
PushPastAggr1Dim(istream & in,std::string & s,ErrorDescriptor * err)508 void PushPastAggr1Dim( istream & in, std::string & s, ErrorDescriptor * err ) {
509 char messageBuf[BUFSIZ];
510 messageBuf[0] = '\0';
511
512 char c;
513 in >> ws;
514 in.get( c );
515
516 if( c == '(' ) {
517 s += c;
518 in.get( c );
519 while( in.good() && ( c != ')' ) ) {
520 if( c == '(' ) {
521 err->GreaterSeverity( SEVERITY_WARNING );
522 sprintf( messageBuf, "Invalid aggregate value.\n" );
523 err->AppendToDetailMsg( messageBuf );
524 }
525
526 if( c == STRING_DELIM ) {
527 in.putback( c );
528 PushPastString( in, s, err );
529 } else {
530 s += c;
531 }
532 in.get( c );
533 }
534 if( c != ')' ) {
535 err->GreaterSeverity( SEVERITY_INPUT_ERROR );
536 sprintf( messageBuf, "Invalid aggregate value.\n" );
537 err->AppendToDetailMsg( messageBuf );
538 s.append( ")" );
539 } else {
540 s += c;
541 }
542 }
543 }
544
545 /**
546 * FindStartOfInstance reads to the beginning of an instance marked by #
547 * it copies what is read to the std::string inst. It leaves the # on the
548 * istream.
549 */
FindStartOfInstance(istream & in,std::string & inst)550 Severity FindStartOfInstance( istream & in, std::string & inst ) {
551 char c = 0;
552 ErrorDescriptor errs;
553 SDAI_String tmp;
554
555 while( in.good() ) {
556 in >> c;
557 switch( c ) {
558 case '#': // found char looking for.
559 in.putback( c );
560 return SEVERITY_NULL;
561
562 case '\'': // get past the string
563 in.putback( c );
564 tmp.STEPread( in, &errs );
565 inst.append( tmp.c_str() );
566 break;
567
568 case '\0': // problem in input ?
569 return SEVERITY_INPUT_ERROR;
570
571 default:
572 inst += c;
573 }
574 }
575 return SEVERITY_INPUT_ERROR;
576 }
577
578 /**
579 * SkipInstance reads in an instance terminated with ;. it copies
580 * what is read to the std::string inst.
581 */
SkipInstance(istream & in,std::string & inst)582 Severity SkipInstance( istream & in, std::string & inst ) {
583 char c = 0;
584 ErrorDescriptor errs;
585 SDAI_String tmp;
586
587 while( in.good() ) {
588 in >> c;
589 switch( c ) {
590 case ';': // end of instance reached
591 return SEVERITY_NULL;
592
593 case '\'': // get past the string
594 in.putback( c );
595 tmp.STEPread( in, &errs );
596 inst.append( tmp.c_str() );
597 break;
598
599 case '\0': // problem in input ?
600 return SEVERITY_INPUT_ERROR;
601
602 default:
603 inst += c;
604 }
605 }
606 return SEVERITY_INPUT_ERROR;
607 }
608
609
610 /**
611 // This reads a simple record. It is used when parsing externally mapped
612 // entities to skip to the next entity type name part of the externally mapped
613 // record. It does not expect a semicolon at the end since the pieces in an
614 // external mapping don't have them. If you are reading a simple record in the
615 // form of an internal mapping you will have to read the semicolon.
616 */
SkipSimpleRecord(istream & in,std::string & buf,ErrorDescriptor * err)617 const char * SkipSimpleRecord( istream & in, std::string & buf, ErrorDescriptor * err ) {
618 char c;
619 std::string s;
620
621 in >> ws;
622 in.get( c );
623 if( c == '(' ) { // beginning of record
624 buf += c;
625 while( in.get( c ) && ( c != ')' ) && ( err->severity() > SEVERITY_INPUT_ERROR ) ) {
626 if( c == '\'' ) {
627 in.putback( c );
628 s.clear();
629 PushPastString( in, s, err );
630 buf.append( s.c_str() );
631 } else if( c == '(' ) {
632 in.putback( c );
633 s.clear();
634 PushPastImbedAggr( in, s, err );
635 buf.append( s.c_str() );
636 } else {
637 buf += c;
638 }
639 }
640 if( !in.good() ) {
641 err->GreaterSeverity( SEVERITY_INPUT_ERROR );
642 err->DetailMsg( "File problems reading simple record.\n" );
643 }
644 buf.append( ")" );
645 } else {
646 in.putback( c ); // put back open paren
647 }
648 return const_cast<char *>( buf.c_str() );
649 }
650
651 /**
652 // This reads a part 21 definition of keyword. This includes the name of
653 // entity types. To read a user-defined keyword: read the '!' then call
654 // this function with skipInitWS turned off.
655 **/
ReadStdKeyword(istream & in,std::string & buf,int skipInitWS)656 const char * ReadStdKeyword( istream & in, std::string & buf, int skipInitWS ) {
657 char c;
658 if( skipInitWS ) {
659 in >> ws;
660 }
661
662 while( in.get( c ) && !isspace( c ) && ( isalnum( c ) || ( c == '_' ) ) ) {
663 buf += c;
664 }
665
666 if( in.eof() || in.good() ) {
667 in.putback( c );
668 }
669
670 return const_cast<char *>( buf.c_str() );
671 }
672
673 /***************************
674 This function returns a null terminated const char* for the
675 characters read from the istream up to, but not including
676 the first character found in the set of delimiters, or the
677 whitespace character. It leaves the delimiter on the istream.
678
679 The string is returned in a static buffer, so it will change
680 the next time the function is called.
681
682 Keywords are special strings of characters indicating the instance
683 of an entity of a specific type. They shall consist of uppercase letters,
684 digits, underscore characters, and possibly an exclamation mark.
685 The "!" shall appear only once, and only as the first character.
686 ***************************/
GetKeyword(istream & in,const char * delims,ErrorDescriptor & err)687 const char * GetKeyword( istream & in, const char * delims, ErrorDescriptor & err ) {
688 char c;
689 int sz = 1;
690 static std::string str;
691
692 str = "";
693 in.get( c );
694 while( !( ( isspace( c ) ) || ( strchr( delims, c ) ) ) ) {
695 //check to see if the char is valid
696 if( !( ( isupper( c ) ) ||
697 ( isdigit( c ) ) ||
698 ( c == '_' ) ||
699 ( c == '-' ) || //for reading 'ISO-10303-21'
700 ( ( c == '!' ) && ( sz == 1 ) ) ) ) {
701 cerr << "Error: Invalid character \'" << c <<
702 "\' in GetKeyword.\nkeyword was: " << str << "\n";
703 err.GreaterSeverity( SEVERITY_WARNING );
704 in.putback( c );
705 return const_cast<char *>( str.c_str() );
706 }
707 if( !in.good() ) {
708 break; //BUG: should do something on eof()
709 }
710 str += c;
711 ++sz;
712 in.get( c );
713 }
714 in.putback( c );
715 return const_cast<char *>( str.c_str() );
716 }
717
718 /**
719 * return 1 if found the keyword 'ENDSEC' with optional space and a ';'
720 * otherwise return 0. This gobbles up the input stream until it knows
721 * that it does or does not have the ENDSEC keyword (and semicolon). i.e.
722 * the first character that stops matching the keyword ENDSEC; (including the
723 * semicolon) will be put back onto the istream, everything else will remain
724 * read. It is this way so that checking for the keywd the next time will
725 * start with the correct char or if it doesn't find it the non-matching char
726 * may be what you are looking for next (e.g. someone typed in END; and the
727 * next chars are DATA; for the beginning of the data section).
728 * FIXME putback() doesn't work well on all platforms
729 */
FoundEndSecKywd(istream & in)730 int FoundEndSecKywd( istream & in ) {
731 char c;
732 in >> ws;
733 in.get( c );
734
735 if( c == 'E' ) {
736 in.get( c );
737 if( c == 'N' ) {
738 in.get( c );
739 if( c == 'D' ) {
740 in.get( c );
741 if( c == 'S' ) {
742 in.get( c );
743 if( c == 'E' ) {
744 in.get( c );
745 if( c == 'C' ) {
746 in >> ws;
747 in.get( c );
748 if( c == ';' ) {
749 return 1;
750 } else {
751 in.putback( c );
752 }
753 } else {
754 in.putback( c );
755 }
756 } else {
757 in.putback( c );
758 }
759 } else {
760 in.putback( c );
761 }
762 } else {
763 in.putback( c );
764 }
765 } else {
766 in.putback( c );
767 }
768 } else {
769 in.putback( c );
770 }
771 // error
772 return 0;
773 }
774
775 // Skip over everything in s until the start of a comment. If it is a well
776 // formatted comment append the comment (including delimiters) to
777 // std::string ss. Return a pointer in s just past what was read as a comment.
778 // If no well formed comment was found, a pointer to the null char in s is
779 // returned. If one is found ss is appended with it and a pointer just
780 // past the comment in s is returned. Note* a carraige return ('\n') is added
781 // after the comment that is appended.
ReadComment(std::string & ss,const char * s)782 const char * ReadComment( std::string & ss, const char * s ) {
783 std::string ssTmp;
784
785 if( s ) {
786 int endComment = 0;
787 while( *s && *s != '/' ) {
788 s++; // skip leading everything
789 }
790 if( *s == '/' ) {
791 s++;
792 if( *s == '*' ) { // found a comment
793 ssTmp.append( "/*" );
794 s++;
795 while( *s && !endComment ) {
796 if( *s == '*' ) {
797 ssTmp += *s;
798 s++;
799 if( *s == '/' ) {
800 endComment = 1;
801 ssTmp += *s;
802 ssTmp.append( "\n" );
803 } else {
804 s--;
805 }
806 } else {
807 ssTmp += *s;
808 }
809 s++;
810 }
811 }
812 }
813 if( endComment ) {
814 ss.append( ssTmp.c_str() );
815 }
816 }
817 return s;
818 }
819
820 /***************************
821 * Reads a comment. If there is a comment it is returned as
822 * char * in space provided in std::string s.
823 * If there is not a comment returns null pointer.
824 * After skipping white space it expects a slash followed by
825 * an asterisk (which I didn't type to avoid messing up the
826 * compiler). It ends with asterisk followed by slash.
827 * If first char from 'in' is a slash it will remain read
828 * whether or not there was a comment. If there is no comment
829 * only the slash will be read from 'in'.
830 * FIXME putback() doesn't work well on all platforms
831 ***************************/
ReadComment(istream & in,std::string & s)832 const char * ReadComment( istream & in, std::string & s ) {
833 char c = '\0';
834 in >> ws;
835 in >> c;
836
837 // it looks like a comment so far
838 if( c == '/' ) { // leave slash read from stream
839 in.get( c ); // won't skip space
840 if( c == '*' ) { // it is a comment
841 in >> ws; // skip leading comment space
842 int commentLength = 0;
843
844 // only to keep it from completely gobbling up input
845 while( commentLength <= MAX_COMMENT_LENGTH ) {
846 in.get( c );
847 if( c == '*' ) { // looks like start of end comment
848 in.get( c );
849 if( c == '/' ) { // it is end of comment
850 return s.c_str(); // return comment as a string
851 } else { // it is not end of comment
852 // so store the * and put back the other char
853 s.append( "*" );
854 in.putback( c );
855 commentLength++;
856 }
857 } else {
858 s += c;
859 commentLength++;
860 }
861 } // end while
862 cout << "ERROR comment longer than maximum comment length of "
863 << MAX_COMMENT_LENGTH << "\n"
864 << "Will try to recover...\n";
865 std::string tmp;
866 SkipInstance( in, tmp );
867 return s.c_str();
868 }
869 // leave slash read from stream... assume caller already knew there was
870 // a slash, leave it off stream so they don't think this funct needs
871 // to be called again
872 else { // not a comment
873 in.putback( c ); // put non asterisk char back on input stream
874 }
875 } else { // first non-white char is not a slash
876 in.putback( c ); // put non slash char back on input stream
877 }
878
879 return 0; // no comment string to return
880 }
881
882 /***************************
883 ** Read a Print Control Directive from the istream
884 ** "\F\" == formfeed
885 ** "\N\" == newline
886 ***************************/
ReadPcd(istream & in)887 Severity ReadPcd( istream & in ) {
888 char c;
889 in.get( c );
890 if( c == '\\' ) {
891 in.get( c );
892 if( c == 'F' || c == 'N' ) {
893 in.get( c );
894 if( c == '\\' ) {
895 in.get( c );
896 return SEVERITY_NULL;
897 }
898 }
899 }
900 cerr << "Print control directive expected.\n";
901 return SEVERITY_WARNING;
902 }
903
904
905 /******************************
906 This function reads through token separators
907 from an istream. It returns when a token
908 separator is not the next thing on the istream.
909 The token separators are blanks, explicit print control directives,
910 and comments.
911 Part 21 considers the blank to be the space character,
912 but this function considers blanks to be the return value of isspace(c)
913 ******************************/
ReadTokenSeparator(istream & in,std::string * comments)914 void ReadTokenSeparator( istream & in, std::string * comments ) {
915 char c;
916 std::string s; // used if need to read a comment
917
918 if( in.eof() ) {
919 //BUG: no error message is reported
920 return;
921 }
922
923 while( in ) {
924 in >> ws; // skip white space.
925 c = in.peek(); // look at next char on input stream
926
927 switch( c ) {
928 case '/': // read p21 file comment
929 s.clear();
930 ReadComment( in, s );
931 if( !s.empty() && comments ) {
932 comments->append( "/*" );
933 comments->append( s.c_str() );
934 comments->append( "*/\n" );
935 }
936 break;
937
938 case '\\': // try to read a print control directive
939 ReadPcd( in );
940 break;
941 case '\n':
942 in.ignore();
943 break;
944 default:
945 return;
946 }
947 }
948 }
949