/***************************************************************************** # Copyright (C) 1994-2008 by David Gordon. # All rights reserved. # # This software is part of a beta-test version of the Consed/Autofinish # package. It should not be redistributed or # used for any commercial purpose, including commercially funded # sequencing, without written permission from the author and the # University of Washington. # # This software is provided ``AS IS'' and any express or implied # warranties, including, but not limited to, the implied warranties of # merchantability and fitness for a particular purpose, are disclaimed. # In no event shall the authors or the University of Washington be # liable for any direct, indirect, incidental, special, exemplary, or # consequential damages (including, but not limited to, procurement of # substitute goods or services; loss of use, data, or profits; or # business interruption) however caused and on any theory of liability, # whether in contract, strict liability, or tort (including negligence # or otherwise) arising in any way out of the use of this software, even # if advised of the possibility of such damage. # # Building Consed from source is error prone and not simple which is # why I provide executables. Due to time limitations I cannot # provide any assistance in building Consed. Even if you do not # modify the source, you may introduce errors due to using a # different version of the compiler, a different version of motif, # different versions of other libraries than I used, etc. For this # reason, if you discover Consed bugs, I can only offer help with # those bugs if you first reproduce those bugs with an executable # provided by me--not an executable you have built. # # Modifying Consed is also difficult. Although Consed is modular, # some modules are used by many other modules. Thus making a change # in one place can have unforeseen effects on many other features. # It may takes months for you to notice these other side-effects # which may not seen connected at all. It is not feasable for me to # provide help with modifying Consed sources because of the # potentially huge amount of time involved. # #*****************************************************************************/ #include "tagTypes.h" #include "saveTagTypesFromFile.h" #include "consedParameters.h" #include "fileDefines.h" #include "rwctokenizer.h" #include "assert.h" #include "userDefinedTagFieldType.h" static int nLine; const size_t nMaxLineSize = 1000; static RWCString soLine( (size_t) nMaxLineSize ); static FILE* pTagTypesFile; #define PARSE_PANIC( szMessage ) \ { RWCString soError = "error while parsing tag file "; \ soError += pCP->filFileOfTagTypes_; \ soError += " at line "; \ soError += RWCString( (long) nLine ); \ soError += "\nwith:\n"; \ soError += soLine; \ soError += "\n"; \ soError += szMessage; \ soError += "\nError detected from source file "; \ soError += __FILE__; \ soError += " at "; \ soError += RWCString( (long) __LINE__); \ soError += "\n"; \ fprintf( stderr, "%s", soError.data() ); \ } static void parseColorLine( RWCTokenizer& tok, bool& bOK, RWCString& soColor ) { soColor = tok(); if ( soColor.isNull() ) { PARSE_PANIC( "COLOR: line is missing the color name after it" ); bOK = false; return; } bOK = true; } static void parsePointerFieldLine( RWCTokenizer& tok, bool& bOK, userDefinedTagFieldType*& pFieldType ) { bOK = false; pFieldType->eFieldType_ = userDefinedTagFieldType::ePointer; pFieldType->soFieldName_ = tok(); if ( pFieldType->soFieldName_.isNull() ) { PARSE_PANIC( "in TAG_TYPE block, POINTER_FIELD: line which should be of form POINTER_FIELD: name (type of tag pointed to) (optional ?*+) but name is missing" ); return; } pFieldType->soPointerToWhichTagType_ = tok(); if ( pFieldType->soPointerToWhichTagType_.isNull() ) { PARSE_PANIC( "in TAG_TYPE block, POINTER_FIELD: line which should be of form POINTER_FIELD: name (type of tag pointed to) (optional ?*+) but name of tag pointed to is missing" ); return; } RWCString soRepeatCode = tok(); if ( soRepeatCode.isNull() ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eOnce; } else if ( soRepeatCode == "?" ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eZeroOrOne; } else if ( soRepeatCode == "*" ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eZeroOrMore; } else if ( soRepeatCode == "+" ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eOneOrMore; } else { RWCString soMessage = "in TAG_TYPE block, POINTER_FIELD: line which should be of form POINTER_FIELD: name (type of tag pointed to) (optional ?*+) but optional repeat code is "; soMessage += soRepeatCode; PARSE_PANIC( soMessage ); return; } bOK = true; } static void parseFieldLine( RWCTokenizer& tok, bool& bOK, userDefinedTagFieldType* pFieldType ) { bOK = false; pFieldType->soFieldName_ = tok(); if ( pFieldType->soFieldName_.isNull() ) { PARSE_PANIC( "in TAG_TYPE block, FIELD: line which should be of form FIELD: name type (optional ?*+) but name is missing" ); return; } RWCString soFieldType = tok(); if ( soFieldType.isNull() ) { PARSE_PANIC( "in TAG_TYPE block, on FIELD: line which should be of the form FIELD: name type (optional ?*+) but the type is missing" ); return; } soFieldType.toLower(); if ( soFieldType == "integer" ) { pFieldType->eFieldType_ = userDefinedTagFieldType::eInteger; } else if ( soFieldType == "floating" ) { pFieldType->eFieldType_ = userDefinedTagFieldType::eFloating; } else if ( soFieldType == "string" ) { pFieldType->eFieldType_ = userDefinedTagFieldType::eString; } else if ( soFieldType == "rwcstring" ) { pFieldType->eFieldType_ = userDefinedTagFieldType::eString; } else { RWCString soMessage = "in TAG_TYPE block, on FIELD: line which should be of the form FIELD: name type (optional ?*+) where the type must be one of integer, floating, string, or pointer but instead it is "; soMessage += soFieldType; PARSE_PANIC( soMessage ); return; } RWCString soRepeatCode = tok(); if ( soRepeatCode.isNull() ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eOnce; } else if ( soRepeatCode == "?" ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eZeroOrOne; } else if ( soRepeatCode == "*" ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eZeroOrMore; } else if ( soRepeatCode == "+" ) { pFieldType->eRepeatCode_ = userDefinedTagFieldType::eOneOrMore; } else { RWCString soMessage = "in TAG_TYPE block, on FIELD: line which should be of the form FIELD: name type (optional repeat code ?*+) but the repeat code is "; soMessage += soRepeatCode; PARSE_PANIC( soMessage ); return; } bOK = true; } const int nLookingForTagType = 1; const int nLookingForConsOrRead = 2; const int nLookingForUserCanAdd = 3; const int nLookingForColor = 4; const int nLookingForColorOrField = 5; const int nLookingForColorOrFieldOrEnd = 6; const int nLookingForFieldsOrEnd = 7; #define ifReturn( VARIABLE ) if ( nState == VARIABLE ) return( #VARIABLE ) inline RWCString soStateToString( const int nState ) { ifReturn( nLookingForTagType ); else ifReturn( nLookingForConsOrRead ); else ifReturn( nLookingForUserCanAdd ); else ifReturn( nLookingForColor ); else ifReturn( nLookingForColorOrField ); else ifReturn( nLookingForColorOrFieldOrEnd ); else ifReturn( nLookingForFieldsOrEnd ); else return("unknown" ); } // file looks like this: // // TAG_TYPE // NAME: tag_type // CONS_OR_READ: both // USER_CAN_ADD: yes // COLOR: color1 // COLOR: color2 // FIELD: name type // FIELD: name type // FIELD: name type // END_TAG_TYPE // where "type" is one of integer, floating, string, or pointer static void parseConfigurableTagType( bool& bOK ) { bOK = false; // will be changed if passes all checks RWCString soTagTypeName; RWTValOrderedVector aColors; RWTPtrOrderedVector aUserDefinedTagFieldTypes; tagType::readOrConsensusType myReadOrConsensus; bool bCanBeCreatedBySwiping; // bool bFoundName = false; // bool bFoundConsOrRead = false; // bool bFoundUserCanAdd = false; // bool bFoundColor = false; int nState = nLookingForTagType; while( true ) { char* pStatus = fgets( soLine.sz_, nMaxLineSize, pTagTypesFile ); ++nLine; soLine.nCurrentLength_ = strlen( soLine.sz_ ); if ( pStatus == 0 ) { RWCString soMessage = "premature end of file while in a TAG_TYPE block"; soMessage += " state = "; soMessage += soStateToString( nState ); PARSE_PANIC( "premature end of file while in a TAG_TYPE block" ); return; /* what else can I do other than terminate? */ } RWCTokenizer tok( soLine ); RWCString soKeyword = tok(); soKeyword.toUpper(); if ( nState == nLookingForTagType ) { if ( soKeyword != "NAME:" ) { RWCString soMessage = "in TAG_TYPE block expecting NAME on this line but found "; soMessage += soKeyword; PARSE_PANIC( soMessage ); return; } soTagTypeName = tok(); if ( soTagTypeName.isNull() ) { PARSE_PANIC( "NAME: is not followed by anything but it should be" ); return; } nState = nLookingForConsOrRead; } else if ( nState == nLookingForConsOrRead ) { if ( soKeyword != "CONS_OR_READ:" ) { RWCString soMessage = "in TAG_TYPE block expecting CONS_OR_READ on this line but found "; soMessage += soKeyword; PARSE_PANIC( soMessage ); } RWCString soConsOrRead = tok(); soConsOrRead.toLower(); myReadOrConsensus = tagType::undefined; if ( soConsOrRead == "consensus" ) myReadOrConsensus = tagType::consensus; else if ( soConsOrRead == "read" ) myReadOrConsensus = tagType::read; else if ( soConsOrRead == "both" ) myReadOrConsensus = tagType::both; else { RWCString soMessage = "in TAG_TYPE block on CONS_OR_READ: line expecting consensus, read, or both but found "; soMessage += soConsOrRead; PARSE_PANIC( soMessage ); return; } nState = nLookingForUserCanAdd; } else if ( nState == nLookingForUserCanAdd ) { if ( soKeyword != "USER_CAN_ADD:" ) { RWCString soMessage = "in TAG_TYPE block expecting USER_CAN_ADD: line but instead found "; soMessage += soKeyword; PARSE_PANIC( soMessage ); return; } RWCString soYesNo = tok(); soYesNo.toLower(); if ( soYesNo == "yes" ) { bCanBeCreatedBySwiping = true; } else if ( soYesNo == "no" ) { bCanBeCreatedBySwiping = false; } else { RWCString soMessage = "in TAG_TYPE block, USER_CAN_ADD: line and expecting \"yes\" or \"no\" but found "; soMessage += soYesNo; PARSE_PANIC( soMessage ); return; } nState = nLookingForColor; } else if ( nState == nLookingForColor ) { if ( soKeyword != "COLOR:" ) { RWCString soMessage = "in TAG_TYPE block expecting COLOR: to start this line but found "; soMessage += soKeyword; PARSE_PANIC( soMessage ); return; } RWCString soColor; parseColorLine( tok, bOK, soColor ); if ( !bOK ) return; aColors.insert( soColor ); nState = nLookingForColorOrFieldOrEnd; } else if ( nState == nLookingForColorOrFieldOrEnd ) { if ( soKeyword == "COLOR:" ) { RWCString soColor; parseColorLine( tok, bOK, soColor ); if ( !bOK ) return; aColors.insert( soColor ); // still in state nLookingForColorOrFieldOrEnd } else if ( soKeyword == "FIELD:" || soKeyword == "POINTER_FIELD:" ) { userDefinedTagFieldType* pField = new userDefinedTagFieldType(); if ( soKeyword == "FIELD:" ) { parseFieldLine( tok, bOK, pField ); } else { parsePointerFieldLine( tok, bOK, pField ); } if ( !bOK ) return; aUserDefinedTagFieldTypes.insert( pField ); nState = nLookingForFieldsOrEnd; } else if ( soKeyword == "END_TAG_TYPE" ) { break; } else { RWCString soMessage = "in TAG_TYPE block expecting COLOR: FIELD: or END_TAG_TYPE but instead found "; soMessage += soKeyword; PARSE_PANIC( soMessage ); return; } } else if ( nState == nLookingForFieldsOrEnd ) { if ( soKeyword == "FIELD:" || soKeyword == "POINTER_FIELD:" ) { userDefinedTagFieldType* pField = new userDefinedTagFieldType(); if ( soKeyword == "FIELD:" ) { parseFieldLine( tok, bOK, pField ); } else { parsePointerFieldLine( tok, bOK, pField ); } if ( !bOK ) return; aUserDefinedTagFieldTypes.insert( pField ); // no state change */ } else if ( soKeyword == "END_TAG_TYPE" ) { break; } else { RWCString soMessage = "in TAG_TYPE block expecting FIELD: or POINTER_FIELD: or END_TAG_TYPE: but instead found "; soMessage += soKeyword; PARSE_PANIC( soMessage ); return; } } else { assert( false ); } // if ( nState == ... ) else } // while( true ); // if made it to here, all checks passed bOK = true; RWTPtrOrderedVector aGuiColorText( (size_t) aColors.length() ); for( int nColor = 0; nColor < aColors.length(); ++nColor ) { GuiColorText* pGuiColorText = new GuiColorText( "black", aColors[ nColor ] ); aGuiColorText.insert( pGuiColorText ); } userDefinedTagType* pTagType = new userDefinedTagType( soTagTypeName, aColors[0], aGuiColorText[0], myReadOrConsensus, bCanBeCreatedBySwiping ); // add the other fields that aren't part of the ctor // I do not believe aGuiColorText_ is used (DG, July 2009) pTagType->aGuiColorText_ = aGuiColorText; pTagType->aUserDefinedTagFieldType_ = aUserDefinedTagFieldTypes; tagTypes::pGetTagTypes()->aTagTypes_.insert( pTagType ); } void saveTagTypesFromFile( ) { if ( pCP->filFileOfTagTypes_.isNull() ) return; pTagTypesFile = fopen( pCP->filFileOfTagTypes_.data(), "r" ); if ( !pTagTypesFile ) { fprintf( stderr, "Could not open consed.fileOfTagTypes: %s errno=%d %s\n", pCP->filFileOfTagTypes_.data(), errno, strerror( errno ) ); // Pat Minx prefers to not terminate--just report the error. return; } printf( "reading %s...\n", pCP->filFileOfTagTypes_.data() ); nLine = 0; while( fgets( soLine.sz_, nMaxLineSize, pTagTypesFile ) != 0 ) { ++nLine; soLine.nCurrentLength_ = strlen( soLine.sz_ ); if ( soLine.bIsWhitespace() ) continue; // line should be of the form: // (tag name) (color) (consensus, read, or both) (yes, no) RWCTokenizer tok( soLine ); RWCString soTagType = tok(); if ( soTagType == "TAG_TYPE" ) { bool bOK; parseConfigurableTagType( bOK ); if ( !bOK ) { fprintf( stderr, "FATAL: cannot continue parsing file %s\n", pCP->filFileOfTagTypes_.data() ); return; } continue; } RWCString soColor = tok(); RWCString soReadOrConsensus = tok(); RWCString soCanBeCreatedBySwiping = tok(); if ( soTagType.isNull() || soColor.isNull() || soReadOrConsensus.isNull() || soCanBeCreatedBySwiping.isNull() ) { PARSE_PANIC( "line must be of form: (tag name) (color for displaying) (read, consensus, or both) (yes/no) but the line did not have 4 fields" ); continue; } if ( soReadOrConsensus != "read" && soReadOrConsensus != "consensus" && soReadOrConsensus != "both" ) { PARSE_PANIC( "line must be of form: (tag name) (color for displaying) (read, consensus, or both) (yes/no) but the 3rd field was not read, consensus, or both" ); continue; } if ( soCanBeCreatedBySwiping != "yes" && soCanBeCreatedBySwiping != "no" ) { PARSE_PANIC( "line must be of form: (tag name) (color for displaying) (read, consensus, or both) (yes/no) but the 4th field was not yes or no" ); fclose( pTagTypesFile ); continue; } tagTypes::pGetTagTypes()->assignValidTagType( soTagType, soColor, ( soReadOrConsensus == "read" ? tagType::read : ( soReadOrConsensus == "consensus" ? tagType::consensus : tagType::both ) ), ( soCanBeCreatedBySwiping == "yes" ? true : false ) ); // can be created by swiping } fclose( pTagTypesFile ); tagTypes::pGetTagTypes()->checkingAfterStoringAllTagTypes(); }