/******************************************************************************
* Module    :   Parse Configuration File --- Build the regular expression
*               hierarchy tree by reading data from a configuration file.
*
* Notes     :   Configuration file grammar:
*
*   CONFIG_FILE ::= SEGMENT <EOF>
*                   | SEGMENT CONFIG_FILE
*
*   SEGMENT     ::= 'segment' STRING '{' ID_LINES '}'
*
*   ID_LINES    ::= 'header' '{' PREFIXES '}'
*                   'body' '{' PREFIXES '}'
*
*   PREFIXES    ::= PREFIX
*                   | PREFIX PREFIXES
*
*   PREFIX      ::= STRING '{' ID_PARTS '}'
*
*   ID_PARTS    ::= ID_PART_RE
*                   | ID_PART_RE ID_PARTS
*
*   ID_PART_RE  :=  STRING '{'
*                   'id' NUMBER
*                   'segment' 'number' NUMBER
*                   'segments' NUMBER
*                   'alternate' 'id' NUMBER
*                   'case' CASE_TYPE
*                   '}'
*
*   CASE_TYPE   ::= 'sensitive'
*                   | 'ignore'
*
*   STRING      ::= '"' [ -~]* '"'
*
*   NUMBER      ::= [0-9]+
*
* Author    :   John W. M. Stevens
******************************************************************************/

#include    "compiler.h"

#include    "unpost.h"
#include    "lex.h"
#include    "list.h"
#include    "parse.h"
#include    "regexp.h"
#include    "config.h"
#include    "utils.h"

typedef enum    {
    ID_SUB_EXPR,
    SEG_NO_SUB_EXPR,
    NO_SEGS_SUB_EXPR,
    ALT_ID_SUB_EXPR,
    CASE_SENSITIVITY
} ID_PARTS_IDX;

static  char    ReStr[512];

/*-----------------------------------------------------------------------------
| Routine   :   ParseParts() --- Parse the ID line part number blocks.
|
| Returns   :   A pointer to the root of the ID part regular expression tree.
-----------------------------------------------------------------------------*/

static
PART_RE *ParseParts(void)
{
    register    int     i;
    auto        TOKEN   sym;
    auto        int     tkn;
    auto        int     Elements[5];
    auto        char    *tp;
    auto        PART_RE NewPart;
    auto        LIST    *PartList;

    /*  Create ID list. */
    PartList = CrtList( sizeof( PART_RE ) );

    /*  Get list of part extraction regular expressions.    */
    while ((tkn = Lex( &sym )) == T_DBL_QUOTE)
    {
        /*  Initialize new part re structure.   */
        NewPart.IDStr    = 0;
        NewPart.SegNo    = 0;
        NewPart.NoSegs   = 0;
        NewPart.AltIDStr = 0;
        NewPart.Case     = IGN_CASE;

        /*  Get IDENT line part regular expression string.  */
        for (tp = ReStr; ; tp = ReStr + strlen( ReStr ))
        {
            /*  Copy string from token structure to buffer. */
            strcpy(tp, sym.str);

            /*  Get next token. */
            if ((tkn = Lex( &sym )) != T_DBL_QUOTE)
                break;
        }

        /*  Create ID prefix regular expression string. */
        NewPart.ReExpStr = StrDup( ReStr );

        /*  Check for opening curly brace.  */
        if (tkn != T_L_BRACE)
        {
            ParseErr("expecting '{' to open ID part block.");
            exit( 1 );
        }

        /*  Process components in ANY order.    */
        for (i = ID_SUB_EXPR; i <= CASE_SENSITIVITY; i++)
            Elements[i] = 0;

        for (i = ID_SUB_EXPR; i <= CASE_SENSITIVITY; i++)
        {
            /*  Get a token.    */
            if ((tkn = Lex( &sym )) == T_R_BRACE)
                break;

            /*  Select parse action based on token. */
            switch ( tkn )
            {
            case T_ID:
                /*  Check for duplication.  */
                if (Elements[ID_SUB_EXPR] != 0)
                {
                    ParseErr("duplicate sub-expression numbers.");
                    exit( 1 );
                }

                /*  Get the sub-expression number for the ID sub-string.    */
                if ((tkn = Lex( &sym )) != T_INT_NO)
                {
                    ParseErr("missing sub-expression number.");
                    exit( 1 );
                }
                NewPart.IDStr = sym.no;
                Elements[ID_SUB_EXPR] = 1;
                break;
            case T_SEGMENT:
                /*  Check for duplication.  */
                if (Elements[SEG_NO_SUB_EXPR] != 0)
                {
                    ParseErr("duplicate sub-expression numbers.");
                    exit( 1 );
                }

                /*  Get next token. */
                if ((tkn = Lex( &sym )) != T_NUMBER)
                {
                    ParseErr("missing 'number' after 'segment'.");
                    exit( 1 );
                }

                /*  Get sub-expression number for the segment number
                *   sub-string.
                */
                if ((tkn = Lex( &sym )) != T_INT_NO)
                {
                    ParseErr("missing sub-expression number.");
                    exit( 1 );
                }
                NewPart.SegNo = sym.no;
                Elements[SEG_NO_SUB_EXPR] = 1;
                break;
            case T_SEGMENTS:
                /*  Check for duplication.  */
                if (Elements[NO_SEGS_SUB_EXPR] != 0)
                {
                    ParseErr("duplicate sub-expression numbers.");
                    exit( 1 );
                }

                /*  Get number of segments sub-expression number.   */
                if ((tkn = Lex( &sym )) != T_INT_NO)
                {
                    ParseErr("missing sub-expression number.");
                    exit( 1 );
                }
                NewPart.NoSegs = sym.no;
                Elements[NO_SEGS_SUB_EXPR] = 1;
                break;
            case T_ALTERNATE:
                /*  Check for duplication.  */
                if (Elements[ALT_ID_SUB_EXPR] != 0)
                {
                    ParseErr("duplicate sub-expression numbers.");
                    exit( 1 );
                }

                /*  Get next token. */
                if ((tkn = Lex( &sym )) != T_ID)
                {
                    ParseErr("missing 'id' after 'alternate'.");
                    exit( 1 );
                 }

                /*  Get alternate ID string sub-expression number.  */
                if ((tkn = Lex( &sym )) != T_INT_NO)
                {
                    ParseErr("missing sub-expression number.");
                    exit( 1 );
                }
                NewPart.AltIDStr = sym.no;
                Elements[ALT_ID_SUB_EXPR] = 1;
                break;
            case T_CASE:
                /*  Check for duplication.  */
                if (Elements[CASE_SENSITIVITY] != 0)
                {
                    ParseErr("duplicate sub-expression numbers.");
                    exit( 1 );
                }

                /*  Get case sensitivity type.  */
                if ((tkn = Lex( &sym )) == T_SENSITIVE)
                    NewPart.Case = CASE_SENSITIVE;
                else if (tkn == T_IGNORE)
                    NewPart.Case = IGN_CASE;
                else
                {
                    ParseErr("expected case sensitivity type after 'case'.");
                    exit( 1 );
                }
                Elements[CASE_SENSITIVITY] = 1;
                break;
            default:
                ParseErr( "Unexpected token in ID part block" );
                break;
            }
        }

        /*  Compile string and save pointer to expression tree in
        *   structure, then save structure in segment list.
        */
        NewPart.ReExpr = ReCompile( NewPart.ReExpStr );
        PartList = AppList(PartList, &NewPart);

        /*  Get closing token.  */
        if (tkn != T_R_BRACE)
        {
            /*  Get and discard the right brace.    */
            if ((tkn = Lex( &sym )) != T_R_BRACE)
            {
                ParseErr("expecting '}' to close ID part block.");
                exit( 1 );
            }
        }
    }

    /*  Check closing token.    */
    if (tkn != T_R_BRACE)
    {
        ParseErr("expecting '}' to close ID prefix block.");
        exit( 1 );
    }

    /*  Append a null record.   */
    memset(&NewPart, 0, sizeof( PART_RE ));
    PartList = AppList(PartList, &NewPart);

     /*  Return pointer to list. */
    MemCopy(PartList,
            PartList->List,
            sizeof( PART_RE ) * PartList->NoElems);

    /*  Return pointer to list. */
    return( (PART_RE *) PartList );
}

/*-----------------------------------------------------------------------------
| Routine   :   ParseIDs() --- Parse the ID blocks.
|
| Returns   :   A pointer to the root of the ID regular expression tree.
-----------------------------------------------------------------------------*/

static
IDENT   *ParseIDs(void)
{
    auto        TOKEN       sym;
    auto        int         tkn;
    auto        char        *tp;
    auto        IDENT       NewID;
    auto        LIST        *PreFixList;

    /*  Create ID list. */
    PreFixList = CrtList( sizeof( IDENT ) );

    /*  Get regular expression source string.   */
    while ((tkn = Lex( &sym )) == T_DBL_QUOTE)
    {
        /*  Get IDENT line regular expression string.   */
        for (tp = ReStr; ; tp = ReStr + strlen( ReStr ))
        {
            /*  Copy string from token structure to buffer. */
            strcpy(tp, sym.str);

            /*  Get next token. */
            if ((tkn = Lex( &sym )) != T_DBL_QUOTE)
                break;
        }

        /*  Check for opening curly brace.  */
        if (tkn != T_L_BRACE)
        {
            ParseErr("expecting '{' after ID prefix regular expression.");
            exit( 1 );
        }

        /*  Create ID prefix regular expression string. */
        NewID.ReExprStr = StrDup( ReStr );

        /*  Parse header regular expression configuration block.    */
        NewID.IdParts = ParseParts();

        /*  Compile string and save pointer to expression tree in
        *   structure, then save structure in segment list.
        */
        NewID.ReExpr = ReCompile( NewID.ReExprStr );
        PreFixList = AppList(PreFixList, &NewID);
    }

    /*  Check closing token.    */
    if (tkn != T_R_BRACE)
    {
        ParseErr("expecting '}' to close ID block.");
        exit( 1 );
     }

    /*  Append a null record.   */
    memset(&NewID, 0, sizeof( IDENT ));
    PreFixList = AppList(PreFixList, &NewID);

    /*  Return pointer to list. */
    MemCopy(PreFixList,
            PreFixList->List,
            sizeof( IDENT ) * PreFixList->NoElems);

    /*  Return pointer to list. */
    return( (IDENT *) PreFixList );
}

/*-----------------------------------------------------------------------------
| Routine   :   ReadConfig() --- Read the configuration file.
|
| Inputs    :   CfgFlNm - Name of configuration file name.
|
| Returns   :   A pointer to the root of the SEGMENT regular expression tree.
-----------------------------------------------------------------------------*/

SEGMENT *ReadConfig(char    *CfgFlNm)
{
    auto        TOKEN       sym;
    auto        int         tkn;
    auto        char        *tp;
    auto        SEGMENT     NewSeg;
    auto        LIST        *SegList;

    /*  Open the configuration file.    */
    OpenCfg( CfgFlNm );

    /*  Get token.  */
    if ((tkn = Lex( &sym )) != T_SEGMENT)
    {
        ParseErr("expecting SEGMENT token.");
        exit( 1 );
    }

    /*  Create segment list.    */
    SegList = CrtList( sizeof( SEGMENT ) );

    /*  Process multiple SEGMENT begin line blocks. */
    while (tkn == T_SEGMENT)
    {
        /*  Get regular expression source string.   */
        if ((tkn = Lex( &sym )) != T_DBL_QUOTE)
        {
            ParseErr("expecting regular expression source string.");
            exit( 1 );
        }

        /*  Get SEGMENT begin line regular expression string.   */
        for (tp = ReStr; ; tp = ReStr + strlen( ReStr ))
        {
            /*  Copy string from token structure to buffer. */
            strcpy(tp, sym.str);

            /*  Get next token. */
            if ((tkn = Lex( &sym )) != T_DBL_QUOTE)
                break;
        }

        /*  Check for opening curly brace.  */
         if (tkn != T_L_BRACE)
        {
            ParseErr("expecting '{' to open SEGMENT block.");
            exit( 1 );
        }

        /*  Create SEGMENT regular expression string.   */
        NewSeg.ReExprStr = StrDup( ReStr );

        /*  Get header block prefix.    */
        if ((tkn = Lex( &sym )) != T_HEADER)
        {
            ParseErr("expecting 'header' in SEGMENT block.");
            exit( 1 );
        }

        /*  Get open curly brace.   */
        if ((tkn = Lex( &sym )) != T_L_BRACE)
        {
            ParseErr("expecting '{' to open HEADER block.");
            exit( 1 );
        }

        /*  Get header block.   */
        NewSeg.Header = ParseIDs();

        /*  Get body block prefix.  */
        if ((tkn = Lex( &sym )) != T_BODY)
        {
            ParseErr("expecting 'body' in SEGMENT block.");
            exit( 1 );
        }

        /*  Get open curly brace.   */
        if ((tkn = Lex( &sym )) != T_L_BRACE)
        {
            ParseErr("expecting '{' to open BODY block.");
            exit( 1 );
        }

        /*  Parse body ID block.    */
        NewSeg.Body = ParseIDs();

        /*  Compile string and save pointer to expression tree in
        *   structure, then save structure in SEGMENT begin line list.
        */
        NewSeg.ReExpr = ReCompile( NewSeg.ReExprStr );
        SegList = AppList(SegList, &NewSeg);

        /*  Get closing token.  */
        if ((tkn = Lex( &sym )) != T_R_BRACE)
        {
            ParseErr("expecting '}' to close SEGMENT block.");
            exit( 1 );
        }

        /*  Get next token. */
        if ((tkn = Lex( &sym )) == EOF)
            break;
    }

    /*  Append a null record.   */
    memset(&NewSeg, 0, sizeof( SEGMENT ));
    SegList = AppList(SegList, &NewSeg);

    /*  Return pointer to list. */
    MemCopy(SegList,
            SegList->List,
            sizeof( SEGMENT ) * SegList->NoElems);

    /*  Close the configuration file and return a pointer to the new
    *   configuration tree.
    */
    CloseCfg();
    return( (SEGMENT *) SegList );
}


syntax highlighted by Code2HTML, v. 0.9.1