/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Lexmark Corporation, 1989                                   */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = PRDGPRAW
 *
 * DESCRIPTIVE NAME =
 *
 *
 * VERSION
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   prdg_ParseRawData
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define INCL_32                         /* Convert to C/SET2    CON3201       */
#define INCL_DOSPROCESS                 /* Convert to C/SET2    CON3201       */
#define INCL_DOSSEMAPHORES
#define INCL_GPIPRIMITIVES
#define INCL_WINHEAP
#define INCL_GPIBITMAPS
#include <os2.h>
#undef INCL_DOSPROCESS                  /* Convert to C/SET2    CON3201       */
#undef INCL_DOSSEMAPHORES
#undef INCL_GPIPRIMITIVES
#undef INCL_WINHEAP
#undef INCL_GPIBITMAPS

#define INCL_DDIBUNDLES
#define INCL_DDIFONTSTRUCS
#define INCL_DDIDEFS
#include <pmddi.h>
#undef INCL_DDIBUNDLES
#undef INCL_DDIFONTSTRUCS
#undef INCL_DDIDEFS

#define INCL_WINP_SELECTIVE
#define INCL_WINP_SEI
#include <pmwinx.h>
#undef INCL_WINP_SELECTIVE
#undef INCL_WINP_SEI
#undef INCL_32                          /* Convert to C/SET2    CON3201       */

#define NO_CONSTANT_INCL
#include <prdinclt.h>
#undef NO_CONSTANT_INCL


/**********************************************************************/
/* Set up access to the data about the escape sequences which the     */
/* printer supports.  This function relies on these being in strict   */
/* order.                                                             */
/**********************************************************************/
extern struct EscapeSequence EscapeStrings[];
extern USHORT                MaxEscapeStrings;

#define TFUNC "prdg_ParseRawData"

/**********************************************************************/
/*                                                                    */
/*   FUNCTION: prdg_ParseRawData                                      */
/*                                                                    */
/*   PARAMETERS:                                                      */
/*                                                                    */
/*   lpDCI           DCIData;  Pointer to DC instance data            */
/*   unsigned char   inch;     The next byte in data stream           */
/*                                                                    */
/*   DESCRIPTION:                                                     */
/*                                                                    */
/*   This function parses the stream of raw data which is being       */
/*   passed to the printer so that the driver can handle form feeds   */
/*   correctly.  The function must follow all the escape sequences    */
/*   which occur in the sequence of raw data.                         */
/*                                                                    */
/*   CHANGES:                                                         */
/*                                                                    */
/* This function is table driven (from the table in in the data       */
/* module) so it should hopefully only require this to be changed to  */
/* reflect the escape sequences for a different printer.  If however  */
/* there are escape sequneces which don't fall into the categories    */
/* this parser can handle then extra code will have to be written to  */
/* handle them.  The parser can handle escape sequences with any      */
/* number of unique identifying characters possibly followed by: a    */
/* count then the number of charcters given in the count; a fixed     */
/* number of characters; a stream of characters followed by a         */
/* delimeter.                                                         */
/*                                                                    */
/**********************************************************************/
#if 0
VOID pascal far prdg_ParseRawData(DCIData,inch)

lpDCI          DCIData;
UCHAR           inch;
#endif

VOID prdg_ParseRawData( lpDCI DCIData,                     /* CON3201 */
                        UCHAR inch )                       /* CON3201 */

{
    /******************************************************************/
    /* Local Variables                                                */
    /******************************************************************/
    SHORT         Direction;            /* Variables used in the      */
    USHORT        HiIndex;              /* binary chop routine for    */
    USHORT        LoIndex;              /* searching for a matching   */
    USHORT        Index;                /* escape sequence            */
    USHORT        PrevIndex;
    NPCH          optr;                 /* Pointers to access the     */
    NPCH          nptr;                 /* escape sequence strings    */
    struct EscapeSequence *NewSequence; /* Pointer to an escape       */
                                        /* sequence                   */

    TRACE4(TFUNC, "Entry", FNULL, 0);
 /* TRACE8(TFUNC, "Input character", (char far *)&inch, 1);             */
 /* TRACE8(TFUNC, "Current state",(void far *)&DCIData->ParserState,1); */
    TRACE8(TFUNC, "Input character", (char *)&inch, 1);             /*CON3201*/
    TRACE8(TFUNC, "Current state",(void *)&DCIData->ParserState,1); /*CON3201*/

    /******************************************************************/
    /* Process the input character through the parsing function.      */
    /* Switch depending on which state we are currently in.  One of   */
    /* prdg_Text, prdg_ESC_match, prdg_ESC_n_ignore,                  */
    /* prdg_ESC_d_ignore prdg_ESC_read_lo_count,                      */
    /* prdg_ESC_read_hi_count.                                        */
    /******************************************************************/
    switch (DCIData->ParserState)
    {
    case prdg_Text:
        /**************************************************************/
        /* Text state.  Usual state, handled in line by a macro.  The */
        /* code is included here for completeness only.               */
        /*                                                            */
        /* The FFaction (Form Feed action) state is maintained:       */
        /*                                                            */
        /* - if the character is text (ie >= 0x20) then set it to     */
        /*   prdg_FFstate,                                            */
        /*                                                            */
        /* - if the character is a FF then set it to prdg_FFx0c.      */
        /*                                                            */
        /* If the input character is an escape then start up the      */
        /* sequence matching mode.                                    */
        /**************************************************************/

        if (inch >= 0x20)
            DCIData->FFstate = prdg_FFtext;

        else if (inch == 0x0c)
           DCIData->FFstate = prdg_FFx0c;

        else if (inch == 0x1b)
             {
            /**********************************************************/
            /* The character is an escape so set ParserState and      */
            /* indicate we have not matched a sequence yet by etting  */
            /* ParserSequence to sNULL.                               */
            /**********************************************************/
            DCIData->ParserState = prdg_ESC_match;
            DCIData->ParserSequence = NULL;
        }
             break;

         case prdg_ESC_match:
        /**************************************************************/
        /* Matching an escape sequence so try to match a new          */
        /* character.                                                 */
        /**************************************************************/
        if (!DCIData->ParserSequence)
        {
            /**********************************************************/
            /* ParserSequence is NULL indicating that this is the     */
            /* first character of an escape sequence so use a binary  */
            /* chop to get to the correct area of the table of escape */
            /* sequences (based on the first character of the escape  */
            /* sequence which is the cuurent input character).        */
            /**********************************************************/
            HiIndex   = MaxEscapeStrings;
            LoIndex   = 0;
            Index     = (LoIndex + HiIndex)/2;
            PrevIndex = MaxEscapeStrings;

            /**********************************************************/
            /* while inch does not match the first character of the   */
            /* sequence indicated by Index move up or down the table  */
            /* depending on whether inch is < or > the first          */
            /* character of the escape sequence at Index.             */
            /**********************************************************/
            while (Direction = (inch - *EscapeStrings[Index].ESCString))
            {
                if (Direction > 0)
                {
                    LoIndex = Index;
                }
                else
                {
                    HiIndex = Index;
                };

                PrevIndex = Index;

                if (PrevIndex == (Index = (LoIndex + HiIndex)/2))
                {
                    /**************************************************/
                    /* There is no escape sequence with a first       */
                    /* character matching the current input character */
                    /* so resume text mode.                           */
                    /**************************************************/
                    DCIData->ParserState = prdg_Text;
                    return;
                }
            } /*.. while (Direction = ...no match yet...............*/

            /**********************************************************/
            /* Set up the ParserSequence and ParserString for the     */
            /* first match found.                                     */
            /**********************************************************/
            DCIData->ParserSequence = &EscapeStrings[Index];
            DCIData->ParserString   = EscapeStrings[Index].ESCString;
        };
        /*.. if (!DCIData->ParserSequence) .......................*/

        /**************************************************************/
        /* Loop forever trying to match escape sequences.  First, try */
        /* the new character against the current escape sequence and  */
        /* if it matches then check if it is the end of the sequence  */
        /* and if it is switch to the appropriate matching mode.  If  */
        /* the new character does not match try the next escape       */
        /* sequence (in either ascending or descending order          */
        /* depending on whether the current character was < or > the  */
        /* character we were trying to match it to).  If the new      */
        /* sequence we are trying to match against does not exist (ie */
        /* we are at one end of the table) or it does not match upto  */
        /* (but not including) the position we are currently at then  */
        /* the escape sequence in the raw data we are trying to match */
        /* is invalid so revert to prdg_Text mode.  If it does match  */
        /* upto (but not including) the position we are currently     */
        /* trying to match then go back to try and match.             */
        /**************************************************************/
        for (Direction = sgn(inch - *DCIData->ParserString);;)
        {
            /**********************************************************/
            /* Partway along a sequence, try the new character and if */
            /* it matches then check for end of string.               */
            /**********************************************************/
            if (!(inch - *DCIData->ParserString))
            {
                if (*++DCIData->ParserString != '\0')
                {
                    /**************************************************/
                    /* Escape sequence not finished yet so return and */
                    /* wait for the next character.  Note that this   */
                    /* is where the pointer to the position in the    */
                    /* escape sequence we are checking is updated.    */
                    /**************************************************/
                    return;
                }
                else
                {
                    /**************************************************/
                    /* The escape sequence has matched till the end   */
                    /* so break to the next section which will take   */
                    /* the appropriate action.                        */
                    /**************************************************/
                    break;
                }
            } /*.. if (!(inch - *DCIData->ParserString)) ...match........*/

                 else
            {
                /******************************************************/
                /* The current sequence does not match so we must try */
                /* another sequence.  Direction determines which way  */
                /* in the table we should go.                         */
                /******************************************************/
                NewSequence = DCIData->ParserSequence + Direction;

                if ( (NewSequence < EscapeStrings) ||
                     (NewSequence > &EscapeStrings[ MaxEscapeStrings-1 ])
                   )
                {
                         /**************************************************/
                    /* The new sequence is beyond one end of the      */
                    /* table so revert to prdg_Text mode because we   */
                    /* will not be able to find a match.              */
                    /**************************************************/
                    DCIData->ParserState = prdg_Text;
                    return;
                }

                /******************************************************/
                /* Check that all the characters in the new escape    */
                /* sequence upto (but not including) the current one  */
                /* match the old escape sequence (because those       */
                /* characters from the old escape sequence have       */
                /* already been matched to the raw data).             */
                /******************************************************/
                for ( optr = DCIData->ParserSequence->ESCString,
                      nptr = NewSequence->ESCString;
                           optr < DCIData->ParserString; ++optr,++nptr)
                {
                    if (*nptr != *optr)
                    {
                        /**********************************************/
                        /* If the new sequence does not match the old */
                        /* then a match is not possible so return.    */
                        /**********************************************/
                        DCIData->ParserState = prdg_Text;
                        return;
                    }
                }

                /******************************************************/
                /* The new sequence is correct upto the character     */
                /* before the current character so loop back and      */
                /* check the current character.                       */
                /******************************************************/
                DCIData->ParserSequence = NewSequence;
                DCIData->ParserString   = nptr;

            } /*.. else ! (!(inch - *DCIData->ParserString.no match......*/

        } /*.. for ( ... ;;) ....for ever................................*/

        /**************************************************************/
        /* The escape sequence has been matched from our table of     */
        /* escape sequences so take the appropriate action for the    */
        /* particular sequence.                                       */
        /**************************************************************/
        TRACE8(TFUNC, "Match action",       \
                     /* (void far *)&DCIData->ParserSequence->ESCAction,1); */
                        (void *)&DCIData->ParserSequence->ESCAction,1); /*CON3201*/

        switch (DCIData->ParserSequence->ESCAction)
        {
        case prdg_ActNull:
             /*********************************************************/
             /* No further action so revert to prdg_Text mode         */
             /*********************************************************/
             DCIData->ParserState = prdg_Text;
             break;

        case prdg_ActDelimited:
             /*********************************************************/
             /* Ignore subsequent characters upto a specified         */
             /* delimeter.                                            */
             /*********************************************************/
             DCIData->ParserState     = prdg_ESC_d_ignore;
             DCIData->ParserDelimiter = (char)DCIData->ParserSequence->ESCValue;
             break;

        case prdg_ActConstIgnore:
             /*********************************************************/
             /* Ignore a specified number of characters.              */
             /*********************************************************/
             DCIData->ParserState = prdg_ESC_n_ignore;
             DCIData->ParserCount = DCIData->ParserSequence->ESCValue;
             break;

        case prdg_ActCountIgnore:
             /*********************************************************/
             /* A two byte count follows so prepare to read it in.    */
             /*********************************************************/
             DCIData->ParserState = prdg_ESC_read_lo_count;
             break;

        case prdg_ActFF:
             /*********************************************************/
             /* A special action for recognising the 0x1b6f "No       */
             /* Formfeed" sequence                                    */
             /*********************************************************/
             DCIData->ParserState = prdg_Text;
             DCIData->FFstate     = prdg_FFx1b6f;
             break;
        }
        /*.. switch (DCIData->ParserSequence->ESCAction) ................*/

        break;

    case prdg_ESC_n_ignore:
        /**************************************************************/
        /* Ignoring n characters.  Decrement the count, move back to  */
        /* text state if all ignored.                                 */
        /**************************************************************/
        if (!(--DCIData->ParserCount))
            DCIData->ParserState = prdg_Text;
        break;

    case prdg_ESC_d_ignore:
        /**************************************************************/
        /* Ignoring up to a delimiter.  If this is it, then stop      */
        /* ignoring.                                                  */
        /**************************************************************/
        if (inch == DCIData->ParserDelimiter)
             DCIData->ParserState = prdg_Text;
        break;

    case prdg_ESC_read_lo_count:
        /**************************************************************/
        /* Reading first byte of count.  Save it, advance state.      */
        /**************************************************************/
        DCIData->ParserCount = (USHORT)inch;
        DCIData->ParserState = prdg_ESC_read_hi_count;
        break;

    case prdg_ESC_read_hi_count:
        /**************************************************************/
        /* Reading second byte of count.  Save it, move to ignore a   */
        /* specified number of characters if there are any.           */
        /**************************************************************/
        DCIData->ParserCount += 256*(USHORT)inch;
        if (DCIData->ParserCount)
            DCIData->ParserState = prdg_ESC_n_ignore;
        else
            DCIData->ParserState = prdg_Text;
        break;

     };
     /*.. switch (DCIData->ParserState) ...............................*/

}; /* prdg_ParseRawData */

#undef TFUNC
