MHParseNode *MHParseText::DoParse() { MHParseNode *pRes = NULL; try { switch (m_nType) { case PTStartSection: // Open curly bracket { NextSym(); // Should be followed by a tag. if (m_nType != PTTag) Error("Expected ':' after '{'"); MHPTagged *pTag = new MHPTagged(m_nTag); pRes = pTag; NextSym(); while (m_nType != PTEndSection) { pTag->AddArg(DoParse()); } NextSym(); // Remove the close curly bracket. break; } case PTTag: // Tag on its own. { int nTag = m_nTag; MHPTagged *pTag = new MHPTagged(nTag); pRes = pTag; NextSym(); switch (nTag) { case C_ITEMS: case C_LINK_EFFECT: case C_ACTIVATE: case C_ADD: case C_ADD_ITEM: case C_APPEND: case C_BRING_TO_FRONT: case C_CALL: case C_CALL_ACTION_SLOT: case C_CLEAR: case C_CLONE: case C_CLOSE_CONNECTION: case C_DEACTIVATE: case C_DEL_ITEM: case C_DESELECT: case C_DESELECT_ITEM: case C_DIVIDE: case C_DRAW_ARC: case C_DRAW_LINE: case C_DRAW_OVAL: case C_DRAW_POLYGON: case C_DRAW_POLYLINE: case C_DRAW_RECTANGLE: case C_DRAW_SECTOR: case C_FORK: case C_GET_AVAILABILITY_STATUS: case C_GET_BOX_SIZE: case C_GET_CELL_ITEM: case C_GET_CURSOR_POSITION: case C_GET_ENGINE_SUPPORT: case C_GET_ENTRY_POINT: case C_GET_FILL_COLOUR: case C_GET_FIRST_ITEM: case C_GET_HIGHLIGHT_STATUS: case C_GET_INTERACTION_STATUS: case C_GET_ITEM_STATUS: case C_GET_LABEL: case C_GET_LAST_ANCHOR_FIRED: case C_GET_LINE_COLOUR: case C_GET_LINE_STYLE: case C_GET_LINE_WIDTH: case C_GET_LIST_ITEM: case C_GET_LIST_SIZE: case C_GET_OVERWRITE_MODE: case C_GET_PORTION: case C_GET_POSITION: case C_GET_RUNNING_STATUS: case C_GET_SELECTION_STATUS: case C_GET_SLIDER_VALUE: case C_GET_TEXT_CONTENT: case C_GET_TEXT_DATA: case C_GET_TOKEN_POSITION: case C_GET_VOLUME: case C_LAUNCH: case C_LOCK_SCREEN: case C_MODULO: case C_MOVE: case C_MOVE_TO: case C_MULTIPLY: case C_OPEN_CONNECTION: case C_PRELOAD: case C_PUT_BEFORE: case C_PUT_BEHIND: case C_QUIT: case C_READ_PERSISTENT: case C_RUN: case C_SCALE_BITMAP: case C_SCALE_VIDEO: case C_SCROLL_ITEMS: case C_SELECT: case C_SELECT_ITEM: case C_SEND_EVENT: case C_SEND_TO_BACK: case C_SET_BOX_SIZE: case C_SET_CACHE_PRIORITY: case C_SET_COUNTER_END_POSITION: case C_SET_COUNTER_POSITION: case C_SET_COUNTER_TRIGGER: case C_SET_CURSOR_POSITION: case C_SET_CURSOR_SHAPE: case C_SET_DATA: case C_SET_ENTRY_POINT: case C_SET_FILL_COLOUR: case C_SET_FIRST_ITEM: case C_SET_FONT_REF: case C_SET_HIGHLIGHT_STATUS: case C_SET_INTERACTION_STATUS: case C_SET_LABEL: case C_SET_LINE_COLOUR: case C_SET_LINE_STYLE: case C_SET_LINE_WIDTH: case C_SET_OVERWRITE_MODE: case C_SET_PALETTE_REF: case C_SET_PORTION: case C_SET_POSITION: case C_SET_SLIDER_VALUE: case C_SET_SPEED: case C_SET_TIMER: case C_SET_TRANSPARENCY: case C_SET_VARIABLE: case C_SET_VOLUME: case C_SPAWN: case C_STEP: case C_STOP: case C_STORE_PERSISTENT: case C_SUBTRACT: case C_TEST_VARIABLE: case C_TOGGLE: case C_TOGGLE_ITEM: case C_TRANSITION_TO: case C_UNLOAD: case C_UNLOCK_SCREEN: case C_CONTENT_REFERENCE: case C_TOKEN_GROUP_ITEMS: case C_POSITIONS: case C_MULTIPLEX: { // These are parenthesised in the text form. We have to remove the // parentheses otherwise we will return a sequence which will not be // be compatible with the binary form. if (m_nType != PTStartSeq) Error("Expected '('"); NextSym(); while (m_nType != PTEndSeq) pTag->AddArg(DoParse()); NextSym(); // Remove the close parenthesis. break; } case C_ORIGINAL_CONTENT: case C_NEW_GENERIC_BOOLEAN: case C_NEW_GENERIC_INTEGER: case C_NEW_GENERIC_OCTETSTRING: case C_NEW_GENERIC_OBJECT_REF: case C_NEW_GENERIC_CONTENT_REF: case C_ORIGINAL_VALUE: // These always have an argument which may be a tagged item. { // Is it always the case that there is at least one argument so if we haven't // had any arguments yet we should always process a tag as an argument? pTag->AddArg(DoParse()); break; } default: // This can be followed by an int, etc but a new tag is dealt with by the caller. while (m_nType == PTBool ||m_nType == PTInt || m_nType == PTString || m_nType == PTEnum || m_nType == PTStartSeq) { pTag->AddArg(DoParse()); } } break; } case PTInt: { pRes = new MHPInt(m_nInt); NextSym(); break; } case PTBool: { pRes = new MHPBool(m_fBool); NextSym(); break; } case PTString: { MHOctetString str; str.Copy(MHOctetString((const char *)m_String, m_nStringLength)); pRes = new MHPString(str); NextSym(); break; } case PTEnum: { pRes = new MHPEnum(m_nInt); NextSym(); break; } case PTNull: { pRes = new MHPNull; NextSym(); break; } case PTStartSeq: // Open parenthesis. { MHParseSequence *pSeq = new MHParseSequence; pRes = pSeq; NextSym(); while (m_nType != PTEndSeq) pSeq->Append(DoParse()); NextSym(); // Remove the close parenthesis. break; } default: Error("Unexpected symbol"); } return pRes; } catch (...) { delete(pRes); throw; } }
// Simple recursive parser for ASN1 BER. MHParseNode *MHParseBinary::DoParse() { unsigned char ch; // Tag class enum { Universal, Context/*, Pseudo*/ } tagClass = Universal; // Byte count of end of this item. Set to INDEFINITE_LENGTH if the length is Indefinite. int endOfItem; unsigned int tagNumber = 0; // Read the first character. ch = GetNextChar(); // ASN1 Coding rules: Top two bits (0 and 1) indicate the tag class. // 0x00 - Universal, 0x40 - Application, 0x80 - Context-specific, 0xC0 - Private // We only use Universal and Context. switch (ch & 0xC0) { case 0x00: // Universal tagClass = Universal; break; case 0x80: tagClass = Context; break; default: MHERROR(QString("Invalid tag class = %1").arg(ch, 0, 16)); } // Bit 2 indicates whether it is a simple or compound type. Not used. // Lower bits are the tag number. tagNumber = ch & 0x1f; if (tagNumber == 0x1f) // Except that if it is 0x1F then the tag is encoded in the following bytes. { tagNumber = 0; do { ch = GetNextChar(); tagNumber = (tagNumber << 7) | (ch & 0x7f); } while (ch & 0x80); // Top bit set means there's more to come. } // Next byte is the length. If it is less than 128 it is the actual length, otherwise it // gives the number of bytes containing the length, except that if this is zero the item // has an "indefinite" length and is terminated by two zero bytes. ch = GetNextChar(); if (ch & 0x80) { int lengthOfLength = ch & 0x7f; if (lengthOfLength == 0) { endOfItem = INDEFINITE_LENGTH; } else { endOfItem = 0; while (lengthOfLength--) { ch = GetNextChar(); endOfItem = (endOfItem << 8) | ch; } endOfItem += m_p; } } else { endOfItem = ch + m_p; } if (tagClass == Context) { MHPTagged *pNode = new MHPTagged(tagNumber); try { // The argument here depends on the particular tag we're processing. switch (tagNumber) { case C_MULTIPLE_SELECTION: case C_OBSCURED_INPUT: case C_INITIALLY_AVAILABLE: case C_WRAP_AROUND: case C_TEXT_WRAPPING: case C_INITIALLY_ACTIVE: case C_MOVING_CURSOR: case C_SHARED: case C_ENGINE_RESP: case C_TILING: case C_BORDERED_BOUNDING_BOX: { // BOOL // If there is no argument we need to indicate that so that it gets // the correct default value. if (m_p != endOfItem) { int intVal = ParseInt(endOfItem); // May raise an exception pNode->AddArg(new MHPBool(intVal != 0)); } break; } case C_INPUT_TYPE: case C_SLIDER_STYLE: case C_TERMINATION: case C_ORIENTATION: case C_HORIZONTAL_JUSTIFICATION: case C_BUTTON_STYLE: case C_START_CORNER: case C_LINE_ORIENTATION: case C_VERTICAL_JUSTIFICATION: case C_STORAGE: { // ENUM if (m_p != endOfItem) { int intVal = ParseInt(endOfItem); // May raise an exception pNode->AddArg(new MHPEnum(intVal)); } break; } case C_INITIAL_PORTION: case C_STEP_SIZE: case C_INPUT_EVENT_REGISTER: case C_INITIAL_VALUE: case C_IP_CONTENT_HOOK: case C_MAX_VALUE: case C_MIN_VALUE: case C_LINE_ART_CONTENT_HOOK: case C_BITMAP_CONTENT_HOOK: case C_TEXT_CONTENT_HOOK: case C_STREAM_CONTENT_HOOK: case C_MAX_LENGTH: case C_CHARACTER_SET: case C_ORIGINAL_TRANSPARENCY: case C_ORIGINAL_GC_PRIORITY: case C_LOOPING: case C_ORIGINAL_LINE_STYLE: case C_STANDARD_VERSION: case C_ORIGINAL_LINE_WIDTH: case C_CONTENT_HOOK: case C_CONTENT_CACHE_PRIORITY: case C_COMPONENT_TAG: case C_ORIGINAL_VOLUME: case C_PROGRAM_CONNECTION_TAG: case C_CONTENT_SIZE: { // INT if (m_p != endOfItem) { int intVal = ParseInt(endOfItem); // May raise an exception pNode->AddArg(new MHPInt(intVal)); } break; } case C_OBJECT_INFORMATION: case C_CONTENT_REFERENCE: case C_FONT_ATTRIBUTES: case C_CHAR_LIST: case C_NAME: case C_ORIGINAL_LABEL: { // STRING // Unlike INT, BOOL and ENUM we can't distinguish an empty string // from a missing string. MHOctetString str; ParseString(endOfItem, str); pNode->AddArg(new MHPString(str)); break; } default: { // Everything else has either no argument or is self-describing // TODO: Handle indefinite length. if (endOfItem == INDEFINITE_LENGTH) { MHERROR("Indefinite length arguments are not implemented"); } while (m_p < endOfItem) { pNode->AddArg(DoParse()); } } } } catch (...) { // Memory clean-up delete pNode; throw; } return pNode; } else // Universal - i.e. a primitive type. { // Tag values switch (tagNumber) { case U_BOOL: // Boolean { int intVal = ParseInt(endOfItem); return new MHPBool(intVal != 0); } case U_INT: // Integer { int intVal = ParseInt(endOfItem); return new MHPInt(intVal); } case U_ENUM: // ENUM { int intVal = ParseInt(endOfItem); return new MHPEnum(intVal); } case U_STRING: // String { MHOctetString str; ParseString(endOfItem, str); return new MHPString(str); } case U_NULL: // ASN1 NULL { return new MHPNull; } case U_SEQUENCE: // Sequence { MHParseSequence *pNode = new MHParseSequence(); if (endOfItem == INDEFINITE_LENGTH) { MHERROR("Indefinite length sequences are not implemented"); } try { while (m_p < endOfItem) { pNode->Append(DoParse()); } } catch (...) { // Memory clean-up if error. delete pNode; throw; } return pNode; } default: MHERROR(QString("Unknown universal %1").arg(tagNumber)); } } }