/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "src/dev/dasd/os2aspi/cmdparse.c, aspi, r206 93/03/20";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = ASPIDATA.C
 *
 * DESCRIPTIVE NAME = OS2ASPI.DMD - OS/2 ASPI Device Manager
 *                    ADD CONFIG.SYS Command Line Parser Helper Routine
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE: 2/11/92
 *
 * DESCRIPTION : ASPI Device Manager - This module consists of the
 *               Command_Parser Function and its associated local
 *               routines.  For detailed description of the
 *               Command_Parser interface refer to the CMDPROTO.H
 *               file.
 *
 *   EXTERNAL REFERENCES:                                                       *
 *                        BASEDEF.H  - Type Definitions (OS2.H)                 *
 *                        CMDPARSE.H - Command_Parser Definitions               *
 *                        CMDPROTO.H - Command_Parser Prototype                 *
 *
*/
#if !(defined(OS2_INCLUDED))
    #define INCL_NOBASEAPI
    #define INCL_NOPMAPI
    #include "OS2.H"
#endif

#include <cmdphdr.h>
#include <cmdproto.h>

#define TOKVBUF_LEN 255
#define UNDEFINED   -1

PSZ        pcmdline1, pcmdline_slash, pcmdline_start;
INT        tokv_index, state_index, length;
CHARBYTE   tokvbuf[TOKVBUF_LEN];
POPT       pend_option, ptable_option;
BYTE       *poutbuf1, *poutbuf_end;
CC         cc;

//
//    Command_Parser -  external entry point into this module
//

CC FAR Command_Parser(pCmdLine,pOptTable,pOutBuf,OutBuf_Len)

PSZ           pCmdLine;
POPTIONTABLE  pOptTable;
PBYTE         pOutBuf;
USHORT        OutBuf_Len;

{
   USHORT j, end_index;

   if (OutBuf_Len < (TOKL_ID_END + TOK_MIN_LENGTH))
   {
      cc.ret_code = BUF_TOO_SMALL_ERR;
      cc.err_index = 0;
      return (cc);
   }

   poutbuf_end = pOutBuf + OutBuf_Len;

   poutbuf1 = pOutBuf;
   for (poutbuf1 = pOutBuf;
        (poutbuf1 < poutbuf_end);
        poutbuf1++)
       *poutbuf1 = 0;
   poutbuf1 = pOutBuf;

   Insert_End_Token();

   //
   //  Locate the last entry in the Option Table. This special entry
   //  defines whether or not an option is required based on the index
   //  in the state table.
   //
   for (end_index = 0 ;
        (pOptTable->poption[end_index]->id != TOK_ID_END);
        end_index++);
   pend_option = pOptTable->poption[end_index];

   //
   //  Setup the initial index into the state table.
   //
   state_index = pOptTable->entry_state;
   if (!Validate_State_Index(pOptTable->max_states))
      return (cc);

   //
   //  On return from Locate_First_Slash call pcmdline_slash
   //  contains the ptr to the / in the command line.
   //
   pcmdline_start = pCmdLine;
   pcmdline1 = pCmdLine;
   if (!Locate_First_Slash())
      return (cc);

   for (j=0;j < end_index;j++)
   {
      //
      //  Locate valid options in Option Table, based on state table index.
      //
      if (pOptTable->poption[j]->state[state_index] != E)
      {
        //
        //  Found a valid option. Check to see if this is the option
        //  entered at this point in command line.
        //
        ptable_option = pOptTable->poption[j];
        length = strlength(ptable_option->string);
        if (strncmp(pcmdline_slash,ptable_option->string, length) == TRUE)
        {

           //
           // Found the command line option.  Now, syntax check its
           // associated value.
           //
           if (!Parse_Option_Value())
              return (cc);

           //
           // No syntax err detected.  Now, insert the option and its
           // associated value into the output buffer in token format.
           //
           if (!Insert_Token())
              return (cc);

           //
           //  Setup next index into the state table.
           //
           state_index = ptable_option->state[state_index];
           if (!Validate_State_Index(pOptTable->max_states))
              return (cc);

           //
           //  Setup cmdline_slash to point the the next / (option) in
           //  the command line.
           //  Parsing stops once either an invalid character is
           //  found on the command line or the end of the command line
           //  is detected.
           //
           if (!Locate_Next_Slash())
              return (cc);

           //
           // Setup for option search. Point to the top of the Option Table.
           //
           j = -1;

        } /* endif */

      } /* endif */

   } /* endfor */


   if (pend_option->state[state_index] == R)
   {
   //
   // A required option was not found on the command line.
   //
      cc.ret_code = REQ_OPT_ERR;
   }
   else
   {
   //
   // Characters on the command line are not defined in the Option Table
   // as a valid option.  All options must start with a / character.
   //
      cc.ret_code = INVALID_OPT_ERR;
   }
   cc.err_index = pcmdline_slash-pCmdLine;
   return (cc);

}

/*******************************************************************************
*                                                                              *
*   FUNCTION: Insert the end of token marker into the output buffer            *
*                                                                              *
*******************************************************************************/
VOID NEAR Insert_End_Token()
{
     *poutbuf1 = TOKL_ID_END;
     *(poutbuf1+1) = TOK_ID_END;
     return;
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: Locate the / on the command line.  All characters entered prior  *
*             to the first / are ignored.  This allows the parser to bypass    *
*             the BASEDEV = xxxxxxxx.xxx portion of the command line.          *
*                                                                              *
*******************************************************************************/
BOOL NEAR Locate_First_Slash()
{


     while ((*pcmdline1 != '\0') &&
            (*pcmdline1 != '\n') &&
            (*pcmdline1 != '\r'))
     {
         if (*pcmdline1 == '/')
         {
            pcmdline_slash = pcmdline1;
            return(TRUE);
         }

         pcmdline1++;
     }

     cc.err_index = 0;
     cc.ret_code = NO_OPTIONS_FND_ERR;
     if (pend_option->state[state_index] == R)
        cc.ret_code = REQ_OPT_ERR;

     return(FALSE);
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: Return the length of a string                                    *
*                                                                              *
*******************************************************************************/
INT  NEAR strlength(s)

CHAR FAR *s;
{
     INT i;

     for (i = 0; *s != '\0' ; s++)
         i++;

     return(i);
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: Compare n number of characters in 2 strings, return TRUE if =    *
*             If s1 is in lower case, convert to upper prior to comparing.     *
*                                                                              *
*******************************************************************************/
BOOL NEAR strncmp(s1,s2,n)

CHAR FAR *s1, FAR *s2;
INT  n;
{
     INT i;
     CHAR temp;

     for (i=0;i < n;i++,s1++,s2++)

         if (*s1 != *s2)
         {
            if ((*s1 >= 'a') && (*s1 <= 'z'))
            {
               temp = *s1 - ' ';
               if (temp == *s2)
                  continue;
            }
            return(FALSE);
         }

     return(TRUE);
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: Parse the command line for the value assigned to located option  *
*                                                                              *
*******************************************************************************/
BOOL  NEAR Parse_Option_Value()
{
      pcmdline1 = pcmdline_slash + length;
      Skip_Over_Blanks();

      for (tokv_index = 0; tokv_index <= TOKVBUF_LEN; tokv_index++)
           tokvbuf[tokv_index].byte_value= 0;

      tokv_index = UNDEFINED;

      cc.ret_code = NO_ERR;
      cc.err_index = 0;

      switch (ptable_option->type)
      {
      case TYPE_0:
           break;

      case TYPE_CHAR:
           char_parser();
           break;

      case TYPE_D:
           d_parser();
           break;

      case TYPE_DD:
           dd_parser();
           break;

      case TYPE_HH:
           hh_parser();
           break;

      case TYPE_HHHH:
           hhhh_parser();
           break;

      case TYPE_FORMAT:
           format_parser();
           break;

      case TYPE_SCSI_ID:
           scsi_id_parser();
           break;

      case TYPE_GEOMETRY:
           geometry_parser();
           break;

      default:
           cc.ret_code = UNDEFINED_TYPE_ERR;

      } /* endswitch */

      if (cc.ret_code != NO_ERR)
      {
         cc.err_index = pcmdline1 - pcmdline_start;
         return(FALSE);
      }

      return(TRUE);
}


/*******************************************************************************
*                                                                              *
*   FUNCTION: Skip over all the blank and tab characters                       *
*                                                                              *
*******************************************************************************/
VOID NEAR Skip_Over_Blanks()
{
     while ((*pcmdline1 == ' ') || (*pcmdline1 == '\t'))
           pcmdline1++;
     return;
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_CHAR option parser - scan till blank,tab,cr,new line or     *
*                                       end of string char                     *
*                                                                              *
*******************************************************************************/
VOID NEAR  char_parser()
{
     while ((*pcmdline1 != '\0') &&
            (*pcmdline1 != '\n') &&
            (*pcmdline1 != '\r') &&
            (*pcmdline1 != '/'))

     {
           tokv_index++;
           tokvbuf[tokv_index].char_value = *pcmdline1;
           pcmdline1++;
     }

     return;
}


/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_D option parser - one digit decimal number (d)              *
*                                                                              *
*******************************************************************************/
VOID NEAR  d_parser()
{
     if ((*pcmdline1 >= '0') && (*pcmdline1 <= '9'))
     {
        tokv_index++;
        tokvbuf[tokv_index].byte_value = *pcmdline1 - '0';
        pcmdline1++;
     }
     else
     {
        cc.ret_code = SYNTAX_ERR;
     }

     return;
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_DD option parser - two digit decimal number (dd)            *
*                                                                              *
*******************************************************************************/
VOID NEAR dd_parser()
{
     INT  i;
     BYTE n;
     BOOL flag;

     n = 0;
     flag = FALSE;
     for ( i = 0; i < 2; i++)
     {
         if ((*pcmdline1 >= '0') && (*pcmdline1 <= '9'))
         {
            n = 10 * n + *pcmdline1 - '0';
            pcmdline1++;
            flag = TRUE;
         }
         else
         {
            //
            // Was at least 1 digit found on the command line?
            //
            if (flag)
               break;

            cc.ret_code = SYNTAX_ERR;
            return;
         }
     }

     tokv_index++;
     tokvbuf[tokv_index].byte_value = n;

     return;
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_HH option parser        hh,hh format (h = hex char)         *
*                                                                              *
*******************************************************************************/
VOID NEAR hh_parser()
{
     //
     //  Convert command line HH char and setup token value buffer
     //
     if (!HH_Char_To_Byte())
        return;

     Skip_Over_Blanks();
     if (*pcmdline1 != ',')
     {
        cc.ret_code = SYNTAX_ERR;
        return;
     }
     pcmdline1++;
     Skip_Over_Blanks();

     //
     //  Convert command line HH char and setup token value buffer
     //
     HH_Char_To_Byte();

     return;
}



/*******************************************************************************
*                                                                              *
*   FUNCTION: Convert HH char to byte value                                    *
*                                                                              *
*******************************************************************************/
BOOL NEAR HH_Char_To_Byte()
{
     BYTE n;
     INT  i;
     BOOL flag;

     n = 0;
     flag = FALSE;

     for ( i = 0; i < 2; i++)
     {
         if ((*pcmdline1 >= '0') && (*pcmdline1 <= '9'))
         {
            n = 16 * n + *pcmdline1 - '0';
            pcmdline1++;
            flag = TRUE;
            continue;
         }

         if ((*pcmdline1 >= 'A') && (*pcmdline1 <= 'F'))
         {
            n = 16 * n + *pcmdline1 - '7';
            pcmdline1++;
            flag = TRUE;
            continue;
         }

         if ((*pcmdline1 >= 'a') && (*pcmdline1 <= 'f'))
         {
            n = 16 * n + *pcmdline1 - 'W';
            pcmdline1++;
            flag = TRUE;
            continue;
         }

         //
         // Was at least 1 hex digit found on the command line?
         //
         if (flag)
            break;

         cc.ret_code = SYNTAX_ERR;
         return(FALSE);

     }

     tokv_index++;
     tokvbuf[tokv_index].byte_value = n;

     return(TRUE);
}


/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_HHHH option parser       hhhh format (h = hex char)         *
*                                                                              *
*******************************************************************************/
VOID NEAR hhhh_parser()
{
     INT  i;
     BOOL flag;
     NUMBER un_number;

     un_number.n = 0;
     flag = FALSE;

     for ( i = 0; i < 4; i++)
     {
         if ((*pcmdline1 >= '0') && (*pcmdline1 <= '9'))
         {
            un_number.n = 16 * un_number.n + *pcmdline1 - '0';
            pcmdline1++;
            flag = TRUE;
            continue;
         }

         if ((*pcmdline1 >= 'A') && (*pcmdline1 <= 'F'))
         {
            un_number.n = 16 * un_number.n + *pcmdline1 - '7';
            pcmdline1++;
            flag = TRUE;
            continue;
         }

         if ((*pcmdline1 >= 'a') && (*pcmdline1 <= 'f'))
         {
            un_number.n = 16 * un_number.n + *pcmdline1 - 'W';
            pcmdline1++;
            flag = TRUE;
            continue;
         }

         //
         // Was at least 1 hex digit found on the command line?
         //
         if (flag)
            break;

         cc.ret_code = SYNTAX_ERR;
         return;


     }

     tokv_index++;
     tokvbuf[tokv_index].byte_value = un_number.two_bytes.byte1;

     tokv_index++;
     tokvbuf[tokv_index].byte_value = un_number.two_bytes.byte2;

     return;
}


/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_FORMAT option parser -  format_table chars accepted         *
*                                                                              *
*******************************************************************************/
VOID NEAR format_parser()
{

     typedef struct _formattable
     {
         PSZ        string;
         USHORT     type;
         NUMBER     MegBytes;

     } FORMATTABLE;

// FORMATTABLE type definitions
#define M_BYTES             1       //accepts MB,mb,Mb,mB, or just the # string
#define K_BYTES             2       //accepts KB,kb,Kb,kB, or just the # string

       INT  str_len, k;
static FORMATTABLE format_table[] =
     {
      { "360",  K_BYTES, 360},
      { "720",  K_BYTES, 720},
      { "1200", K_BYTES, 1200},
      { "1.2",  M_BYTES, 1200},
      { "1440", K_BYTES, 1440},
      { "1.44", M_BYTES, 1440},
      { "2880", K_BYTES, 2880},
      { "2.88", M_BYTES, 2880},
      { "-1"},
     };

     cc.ret_code = SYNTAX_ERR;
     for (k = 0; (format_table[k].string != "-1"); k++)
     {

         str_len = strlength(format_table[k].string);

         if (strncmp(pcmdline1,format_table[k].string, str_len)
            == TRUE)
         {
            pcmdline1 += str_len;
            if (format_table[k].type == K_BYTES)
            {
               if ((*pcmdline1 == 'K') || (*pcmdline1 == 'k'))
               {
                  ++pcmdline1;

                  if ((*(pcmdline1+1) == 'B') || (*(pcmdline1+1) == 'b'))
                     ++pcmdline1;
               }
            }
            else
            {
               if ((*pcmdline1 == 'M') || (*pcmdline1 == 'm'))
               {
                  ++pcmdline1;

                  if ((*(pcmdline1+1) == 'B') || (*(pcmdline1+1) == 'b'))
                     ++pcmdline1;
                }
            }

            tokv_index++;
            tokvbuf[tokv_index].byte_value = format_table[k].MegBytes.two_bytes.byte1;
            tokv_index++;
            tokvbuf[tokv_index].byte_value = format_table[k].MegBytes.two_bytes.byte2;

            cc.ret_code = NO_ERR;
            break;
         }
     }
     return;
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_SCSI_ID option parser - format d and (d,d) accepted         *
*                                          where d = 0 - 7                     *
*                                                                              *
*******************************************************************************/
VOID NEAR scsi_id_parser()
{
     BOOL found_bracket, found_one;

     found_bracket = FALSE;
     found_one = FALSE;

     if (*pcmdline1 == '(')
     {
        found_bracket = TRUE;
        pcmdline1++;
        Skip_Over_Blanks();
     }


     while ((*pcmdline1 >= '0') && (*pcmdline1 <= '7'))
     {
           found_one = TRUE;
           tokv_index++;
           tokvbuf[tokv_index].byte_value = *pcmdline1 - '0';
           pcmdline1++;
           if (!found_bracket)
           {
              tokv_index++;
              tokvbuf[tokv_index].byte_value = 0;
           }

           Skip_Over_Blanks();
           if (*pcmdline1 != ',')
           {
              if (found_bracket)
                 cc.ret_code = SYNTAX_ERR;
              return;
           }

           pcmdline1++;
           Skip_Over_Blanks();
           if (found_bracket)
           {
              if ((*pcmdline1 >= '0') && (*pcmdline1 <= '7'))
              {
                 tokv_index++;
                 tokvbuf[tokv_index].byte_value = *pcmdline1 - '0';
                 pcmdline1++;
                 Skip_Over_Blanks();
                 if (*pcmdline1 != ')')
                 {
                   cc.ret_code = SYNTAX_ERR;
                   return;
                 }
                 pcmdline1++;
                 Skip_Over_Blanks();
                 found_bracket = FALSE;

                 if (*pcmdline1 != ',')
                    return;

                 pcmdline1++;
                 Skip_Over_Blanks();
              }
           }

           if (*pcmdline1 == '(')
           {
              found_bracket = TRUE;
              pcmdline1++;
              Skip_Over_Blanks();
           }
     }

     if (found_bracket)
        cc.ret_code = SYNTAX_ERR;

     if (!found_one)
        cc.ret_code = SYNTAX_ERR;

     return;
}


/*******************************************************************************
*                                                                              *
*   FUNCTION: TYPE_GEOMETRY option parser - dd or (dddd,dddd,dddd) accepted    *
*                                                                              *
*******************************************************************************/
VOID NEAR geometry_parser()
{
     INT  i, counter;
     NUMBER un_number;
     BOOL flag;

     BOOL found_bracket, found_one;

     _asm{int 3};
     if (*pcmdline1 != '(')
     {
        dd_parser();
        return;
     }

     pcmdline1++;
     Skip_Over_Blanks();

     counter = 0;
     while(TRUE)
     {
           un_number.n = 0;
           flag = FALSE;
           for ( i = 0; i < 4; i++)
           {
               if ((*pcmdline1 >= '0') && (*pcmdline1 <= '9'))
               {
                  un_number.n = 10 * un_number.n + *pcmdline1 - '0';
                  pcmdline1++;
                  flag = TRUE;
               }
               else
               {
                  //
                  // Was at least 1 digit found on the command line?
                  //
                  if (flag)
                     break;

                  cc.ret_code = SYNTAX_ERR;
                  return;
               }
           }

           tokv_index++;
           tokvbuf[tokv_index].byte_value = un_number.two_bytes.byte1;
           tokv_index++;
           tokvbuf[tokv_index].byte_value = un_number.two_bytes.byte2;

           Skip_Over_Blanks();
           if (counter == 2)
           {
              if (*pcmdline1 != ')')
                 cc.ret_code = SYNTAX_ERR;
              else
                 pcmdline1++;
              return;
           }


           if (*pcmdline1 != ',')
           {
              cc.ret_code = SYNTAX_ERR;
              return;
           }

           pcmdline1++;
           counter++;
           Skip_Over_Blanks();
     }

     return;
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: Insert the parsed option (token) into the output buffer.         *
*                                                                              *
*******************************************************************************/
BOOL NEAR Insert_Token()
{
    USHORT t, tok_size;

    tok_size = TOK_MIN_LENGTH + tokv_index;
    if ((poutbuf1 + tok_size + TOKL_ID_END) >= poutbuf_end)
    {
       cc.err_index = pcmdline_slash-pcmdline_start;
       cc.ret_code = BUF_TOO_SMALL_ERR;
       return(FALSE);
    }

    *poutbuf1 = tok_size + 1;
    poutbuf1++;
    *poutbuf1 = ptable_option->id;
    poutbuf1++;

    if (tokv_index != UNDEFINED)
    {
       for (t=0;t <= tokv_index;t++)
       {
           *poutbuf1 = tokvbuf[t].byte_value;
            poutbuf1++;
       }
    }

    Insert_End_Token();

    return(TRUE);
}

/*******************************************************************************
*                                                                              *
*   FUNCTION: Locate the next / char.                                          *
*                                                                              *
*******************************************************************************/
BOOL NEAR Locate_Next_Slash()
{

     while ((*pcmdline1 != '\0') &&
            (*pcmdline1 != '\n') &&
            (*pcmdline1 != '\r'))
     {

           if ((*pcmdline1 == ' ') ||
               (*pcmdline1 == '\t'))
           {
              pcmdline1++;
           }
           else
           {
              if (*pcmdline1 == '/')
              {
                 pcmdline_slash = pcmdline1;
                 return(TRUE);
              }
              else
              {
                 cc.ret_code = INVALID_OPT_ERR;
                 cc.err_index = pcmdline1-pcmdline_start;
                 return(FALSE);
              }
           }

     } /* endwhile */

     if (pend_option->state[state_index] == R)
     {
        cc.ret_code = REQ_OPT_ERR;
        cc.err_index = pcmdline1-pcmdline_start;
     }
     else
     {
        cc.ret_code = NO_ERR;
        cc.err_index = 0;
     }
     return(FALSE);
}


/*******************************************************************************
*                                                                              *
*   FUNCTION: Validate the State Index                                         *
*                                                                              *
*******************************************************************************/
BOOL NEAR Validate_State_Index(maxstate)

USHORT  maxstate;
{

   if ((state_index > maxstate) ||
       (state_index < 0))
   {
      cc.ret_code = UNDEFINED_STATE_ERR;
      cc.err_index = 0;
      return(FALSE);
   }

   return(TRUE);
}

