parsing.cpp

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C++ 代码 · 共 1,349 行 · 第 1/3 页

CPP
1,349
字号
/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  .RTF file parsing.
*
****************************************************************************/


#include "parsing.h"
#include "hcerrors.h"
#include <string.h>
#include <ctype.h>


//  Generate the list of supported RTF commands.
#define _COMMAND( n, s ) n
enum com_nums {
#include "../h/commands.h"
};

#undef _COMMAND
#define _COMMAND( n, s ) s
static char const *com_strs[] = {
#include "../h/commands.h"
};


#define TOPIC_LIMIT 512 // Arbitrary limit to topic size.

char const RtfExt[] = ".RTF";


//  FindCommand     --Map command strings to their assigned numbers.
//            Returns (-1) for failure.

static int FindCommand( char const string[] )
{
    int left=0, right=RC_DUMMY-1, _current, result;

    // I use binary search; the list is reasonably small.
    while( right >= left ){
    _current = (left+right)/2;
    result = strcmp( string, com_strs[_current] );
    if( result < 0 ){
        right = _current - 1;
    } else if( result > 0 ){
        left = _current + 1;
    } else if( result == 0 ){
        return _current;
    }
    }
    return -1;
}


#define MAX_STATES  20
#define BLOCK_SIZE  80


//  RTFparser::RTFparser    --Mostly, this function just records
//                where other important objects are.

RTFparser::RTFparser( Pointers *p, InFile *src )
    : _storage( BLOCK_SIZE )
{
    _topFile = p->_topFile;
    _fontFile = p->_fontFile;
    _hashFile = p->_hashFile;
    _keyFile = p->_keyFile;
    _titleFile = p->_titleFile;
    _bitFiles = p->_bitFiles;
    _sysFile = p->_sysFile;

    _input = new Scanner( src );
    _fname = new char[ strlen(src->name()) + 1];
    strcpy( _fname, src->name() );

    _nestLevel = 0;

    _storSize = 0;
    _maxStor = BLOCK_SIZE;

    _tabType = TAB_LEFT;
}


//  RTFparser::~RTFparser

RTFparser::~RTFparser()
{
    delete _input;
    if( _fname ) delete[] _fname;
}


//  RTFparser::skipSection  --Pass over RTF text until the "_nestLevel"
//                decreases.

void RTFparser::skipSection()
{
    int target = _nestLevel-1;
    while( _nestLevel > target ){
    _current = _input->next();
    switch( _current->_type ){
    case TOK_PUSH_STATE:
        ++_nestLevel;
        break;
    case TOK_POP_STATE:
        --_nestLevel;
        break;
    case TOK_END:
        HCWarning( RTF_BADEOF, _fname );
        _wereWarnings = 1;
        return;
    }
    }
    return;
}


//  RTFparser::closeBraces  --Attempt to combine successive font changes.
//                Returns the index of the final font.

uint_16 RTFparser::closeBraces()
{
    uint_16 result = _fontFile->currentFont();
    TokenTypes  t_type;

    for( ;; ){
    t_type = _input->look(1)->_type;
    if( t_type == TOK_PUSH_STATE ){
        _fontFile->push();
        ++_nestLevel;
    } else if( t_type == TOK_POP_STATE &&
               _nestLevel > 0 ){
        result = _fontFile->pop();
        --_nestLevel;
    } else if( t_type == TOK_COMMAND ){
        if( !isFontCommand( _input->look(1), &result ) ){
        break;
        }
    } else {
        break;
    }
    _input->next();
    }

    return result;
}


//  RTFparser::isParCommand --Identify commands which affect paragraph
//                attributes (tab stops, indents, ...)

int RTFparser::isParCommand()
{
    int result=0;
    if( _input->look(1)->_type == TOK_COMMAND ){
    int com_num = FindCommand( _input->look(1)->_text );
    switch( FindCommand( _input->look(1)->_text ) ){
    case RC_BOX:
    case RC_FI:
    case RC_LI:
    case RC_KEEP:
    case RC_KEEPN:
    case RC_PARD:
    case RC_RI:
    case RC_QC:
    case RC_QJ:
    case RC_QL:
    case RC_QR:
    case RC_SA:
    case RC_SB:
    case RC_SL:
    case RC_TX:
        result = 1;
    }
    }
    return result;
}


//  RTFparser::isFontCommand    --Identify a "font" command, AND implement
//                the corresponding font change.
//                The new font is stored in "newfont".

int RTFparser::isFontCommand( Token * tok, uint_16 *newfont )
{
    int result;
    int num = FindCommand( tok->_text );

    // First check for a command which 'toggles' a font attribute.
    switch( num ){
    case RC_B:
    case RC_I:
    case RC_SCAPS:
    case RC_STRIKE:
    case RC_UL:
    case RC_ULDB:
    uint_8  style;
    switch( num ){
    case RC_B: style = FNT_BOLD; break;
    case RC_I: style = FNT_ITALICS; break;
    case RC_SCAPS: style = FNT_SMALL_CAPS; break;
    case RC_STRIKE: style = FNT_STRIKEOUT; break;
    case RC_UL: style = FNT_UNDERLINE; break;
    case RC_ULDB: style = FNT_DBL_UNDER; break;
    }
    if( !tok->_hasValue || tok->_value != 0 ){
        *newfont = _fontFile->setAttribs( style );
    } else {
        *newfont = _fontFile->clearAttribs( style );
    }
    result = 1;
    break;

    default:

    // Now check for commands which change the base font.
    switch( num ){
        case RC_F:
        if( tok->_hasValue ){
        *newfont = _fontFile->selectFont( (short) tok->_value,
                                           tok->_lineNum,
                           _input->file()->name() );
        result = 1;
        } else {
        HCWarning( FONT_NONUM, tok->_lineNum, _fname );
        _wereWarnings = 1;
        tok->_type = TOK_NONE;
        result = 0;
        }
        break;

        case RC_FS:
        if( tok->_hasValue ){
        *newfont = _fontFile->newSize( (uint_8) (tok->_value) );
        result = 1;
        } else {
        HCWarning( RTF_NOARG, (const char *) tok->_text,
                   tok->_lineNum, _fname );
        _wereWarnings = 1;
        tok->_type = TOK_NONE;
        result = 0;
        }
        break;

        case RC_PLAIN:
        *newfont = _fontFile->clearAttribs( 0xFF );
        result = 1;
        break;

        default:
        result = 0;
    }
    }
    return result;
}


//  RTFparser::handleCommand    --Implement non-font-related commands.

void RTFparser::handleCommand()
{
    int com_num = FindCommand( _current->_text );
    uint_8  attribs;

    // two variables we may need to deal with a \par command.
    uint_16 temp_font;
    int     is_new_topic;

    // Certain commands may necessitate a new node in the |TOPIC file.
    if( _writeState == HEADER ){
    switch( com_num ){
    case RC_LINE:
    case RC_PAR:
    case RC_SECT:
    case RC_TAB:
    case RC_V:
        _writeState = SCROLL;
        _topFile->newNode(0);
        _topFile->addAttr( TOP_FONT_CHANGE, _curFont );
        _topFile->startScroll();
    }
    }

    switch( com_num ){
    // Sections to skip 'cos they're unused in .HLP files.
    case RC_COLORTBL:   // actually, I should support this one.
    case RC_INFO:
    case RC_STYLESHEET:
    skipSection();
    break;


    // Commands to ignore 'cos they're meaningless in .HLP files.
    case RC_ENDNHERE:
    case RC_FTNBJ:
    case RC_LINEX:
    case RC_SECTD:
    case RC_ULW:
    // do nothing
    break;


    case RC_BOX:    // The "Boxed paragraph" command
    _topFile->setPar( TOP_BORDER, 0x1 );
    break;


    case RC_DEFF:   // The "Set Default Font" command
    if( !_current->_hasValue ){
        HCWarning( RTF_NOARG, (const char *) _current->_text,
                   _current->_lineNum, _fname );
        _wereWarnings = 1;
    } else {
        _defFont = (uint_16) _current->_value;
    }
    break;


    case RC_FI: // The "First Line Indent" command
    if( !_current->_hasValue ){
        HCWarning( RTF_NOARG, (const char *) _current->_text,
                   _current->_lineNum, _fname );
        _wereWarnings = 1;
    } else {
        if( !_topFile->setPar( TOP_FIRST_INDENT, _current->_value ) ){
        HCWarning( TOP_BADARG, _current->_value, _current->_lineNum, _fname );
        _wereWarnings = 1;
        }
    }
    break;


    case RC_FONTTBL: // The "Font Table" command
    if( !_current->_hasValue || _current->_value != 0 ){
        handleFonts();
    }
    break;


    case RC_KEEP:   // The "No LineWrap" command
    _topFile->setPar( TOP_NO_LINE_WRAP );
    break;


    case RC_KEEPN:  // The "Start Non-scroll Area" command.
    if( _writeState == HEADER ){
        _topFile->newNode(0);
        _topFile->addAttr( TOP_FONT_CHANGE, _curFont );
        _topFile->startNonScroll();
        _writeState = NON_SCROLL;
    } else if( _writeState == SCROLL ){
        HCWarning( RTF_LATEKEEPN, _current->_lineNum, _fname );
        _wereWarnings = 1;
    }
    break;


    case RC_LI: // The "Left Indent" command.
    if( !_current->_hasValue ){
        HCWarning( RTF_NOARG, (const char *) _current->_text,
                       _current->_lineNum, _fname );
        _wereWarnings = 1;
    } else {
        if( !_topFile->setPar( TOP_LEFT_INDENT, _current->_value ) ){
        HCWarning( TOP_BADARG, _current->_value, _current->_lineNum, _fname );
        _wereWarnings = 1;
        }
    }
    break;


    case RC_LINE:   // The "Explicit New-Line".
    _topFile->addAttr( TOP_NEW_LINE );
    break;


    case RC_PAGE:   // The "Hard Page".
    // \page ALWAYS signals a new topic.
    HCTick();
    _topFile->newNode( 1 );
    _topFile->clearPar();
    _curFont = _fontFile->clearAttribs( 0xFF );
    _writeState = HEADER;
    break;


    case RC_PAR:        // "Paragraph return".
    case RC_SECT:       // deliberate fall-through
    _topFile->addAttr( TOP_NEW_PAR );
    _lastFont = _fontFile->clearAttribs( 0xFF );
    temp_font = closeBraces();

    // These commands signal a new topic if the next token is
    // a paragraph command, or if the current topic has grown
    // too large for comfort.

    is_new_topic = _topFile->presentSize() >= TOPIC_LIMIT || isParCommand();
    if( is_new_topic || _curFont != temp_font ){
        if( is_new_topic ){
        _topFile->newNode(0);
        }
        _curFont = temp_font;
        int attr = _topFile->addAttr( TOP_FONT_CHANGE, _curFont );
        attribs = _fontFile->getAttribs( _curFont );
        if( attribs & ( FNT_UNDERLINE | FNT_STRIKEOUT |
        FNT_DBL_UNDER ) ){
        _hotlinkStart = attr;
        }
    }
    break;


    case RC_PARD:   // "Set Paragraph Properties to Default"
    _topFile->clearPar();
    if( _writeState == NON_SCROLL ){
        _topFile->startScroll();
        _writeState = SCROLL;
    }
    _tabType = TAB_LEFT;
    break;


    case RC_QC: // Centre Justification
    _topFile->unsetPar( TOP_RIGHT_JUST );

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?