/*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/os2scsi/scinit.c, scsy, basedd, initial 93/03/19";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = SCINIT.C
 *
 * DESCRIPTIVE NAME = OS2SCSI.DMD - OS/2 SCSI.SYS Emulation
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Initialization routines
 *
 *
 *
*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include "os2.h"
#include "strat2.h"
#include "reqpkt.h"
#include "devclass.h"
#include "dhcalls.h"
#include "scb.h"
#include "scsi.h"
#include "iorb.h"
#include "dskinit.h"    // DDD_PARM_LIST
#include "scscsi.h"
#include "scgen.h"
#include "scproto.h"


extern    UNITCB         UnitCB[1];            /* UnitCB allocated here       */
extern    PFN            Device_Help;          /* far ptr to devhelp function */
extern    USHORT         NumDrivers;           /* num of adapter drivers      */
extern    USHORT         NumUnitCBs;           /* num of unit control blocks  */
extern    PVOID          pDataSeg;             /* Our data segment pointer    */
extern    ULONG          plDataSeg;            /* Our data segment pointer    */
extern    UCHAR          Pool[];

#ifdef PARSE
extern    OPTIONTABLE    opttable;
extern    BYTE           outbuf[];
extern    USHORT         ShareAllocation;
extern    PSZ            pcmd_line;
extern    BYTE           tokvbuf[];
extern    PSZ            pcmdline_slash;
#endif

#define   ERR_FNCALL     ((USHORT) 0xFFFF)     /* Indicates error in fn call. */

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  DriveInit                                               *
*                                                                           *
* DESCRIPTIVE NAME: Device Driver Initialize routine                        *
*                                                                           *
* FUNCTION:  This routine sets up unit control block and sets output        *
*            information required in request packet.                        *
*                                                                           *
* ENTRY POINT:      DriveInit                                               *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            Pointer to Init Reuqest Packet                          *
*                                                                           *
* EXIT-NORMAL:      Status code in request packet                           *
*                                                                           *
* EXIT-ERROR:       Status code in request packet                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT far f_DriveInit(pRP)
PRPINITIN  pRP;
{
  USHORT     rc;
  NPBYTE     pNextFreeCB;                 /* ptr to next free control blk     */
  PRPINITOUT pRPO = (PRPINITOUT) pRP;     /* Output for Init RP               */

#ifdef PARSE
  PSZ        pCmdLine = 0L;
  PDDD_PARM_LIST    pDDD_Parm_List = (PDDD_PARM_LIST) pRP->InitArgs;   // Point to initialization vector table.
#endif


  Device_Help = pRP->DevHlpEP;            /* Save ptr to devhelp function     */
  pDataSeg = (PVOID) &pDataSeg;           /* Set up pointer to data segment   */
  OFFSETOF(pDataSeg) = 0;

#ifdef PARSE
  /*----------------------------------------------------------------------
   * Get command line parameters...
   *--------------------------------------------------------------------*/
  pCmdLine = (PSZ) MAKEP(SELECTOROF(pDDD_Parm_List), OFFSETOF(pDDD_Parm_List->cmd_line_args));

  if ( Parse_Command(pCmdLine) )
  {
     pRPO->Unit     = 0;                  /* Set zero for the number of unit  */
     pRPO->CodeEnd  = 0;                  /* Set length of code segment       */
     pRPO->DataEnd  = 0;                  /* Set length of data segment       */
     pRPO->BPBArray = 0;                  /* Set address of BPB ptr array     */

     return(STERR | STDON | 0x0013 /*ERROR_I24_INVALID_PARAMETER*/ );
  }
#endif

  rc = DevHelp_VirtToLin((USHORT) (SELECTOROF(pDataSeg)),
                         (ULONG) (OFFSETOF(pDataSeg)),
                         (PLIN) &plDataSeg);  /* Save lin addr of data seg    */


  NumUnitCBs = Build_UnitCBs();           /* Build unit control blocks        */
  if ((NumUnitCBs!=0) && (NumUnitCBs!=ERR_FNCALL))
  {                                       /* If there's units to control.     */

     pRPO->Unit     = 0;
     pRPO->CodeEnd  = (USHORT)f_DriveInit;/* Set length of code segment       */

     pRPO->DataEnd  = SetAdditionalData();
     pRPO->BPBArray = 0;                  /* Set address of BPB ptr array     */

     return(STDON);                       /* Set status                       */
  }
  else                                    /* no media or Init failure         */
  {
     pRPO->Unit     = 0;                  /* Set zero for the number of unit  */
     pRPO->CodeEnd  = 0;                  /* Set length of code segment       */
     pRPO->DataEnd  = 0;                  /* Set length of data segment       */
     pRPO->BPBArray = 0;                  /* Set address of BPB ptr array     */

     if (NumUnitCBs == 0)                 /* Quiet fail if no units           */
        return( 0x8115 /*ERROR_I24_QUIET_INIT_FAIL*/ );
     else
        return(STDON + STERR);            /* Noisy fail on program exception. */
  }
}

#ifdef PARSE
/*-----------------------------------------------------------------------------
 * Function:   Parse_Command()
 *
 * FUNCTION:   This procedure calls the COMMAND LINE parser, then translates
 *             data into usable format.
 *
 * Inputs:     none
 *
 * Outputs:    none
 *
 * Returns:
 *
 * Remarks:    uses global outbuf, etc.
 *---------------------------------------------------------------------------*/

BOOL NEAR Parse_Command(PSZ pCmdLine)
{
   PBYTE    pBufOut  = (PBYTE) &outbuf;
   USHORT   ret_code;


   ret_code = Command_Line_Parser(pCmdLine, &opttable, outbuf, (USHORT) (OUTBUF_LEN + 1) );

   if (ret_code == NO_OPTIONS_FND_ERR)
   {
      return(FALSE);
   }

   if (ret_code != NO_ERR)
   {
      return(TRUE);
   }

   while (pBufOut[1] != (UCHAR) TOK_ID_END)
   {
      switch (pBufOut[1])
      {
         case TOK_ID_SHARE:
            ShareAllocation = TRUE;
            break;

         default:
            return(TRUE);
            break;

      }       /* end switch */

      pBufOut += pBufOut[0];

   }       /* end while */

   return FALSE;
}
#endif

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  Build_UnitCBs                                           *
*                                                                           *
* DESCRIPTIVE NAME: Build Unit Control Blocks  (UnitCBs)                    *
*                                                                           *
* FUNCTION:  This routine issues DevHelp comand to get each device driver's *
*            entry point and IORB device table request for each driver to   *
*            get information about the device.                              *
*            Unit Control Block are created by these information.           *
*                                                                           *
* ENTRY POINT:      Build_UnitCBs                                           *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:                                                                    *
*                                                                           *
* EXIT-NORMAL:      UnitCBs will be filled up for each physical unit.       *
*                   NumUnitCBs = Number of UnitCBs built                    *
*                                                                           *
* EXIT-ERROR:       0                   => No units to support.             *
*                   ERR_FNCALL (0xFFFF) => DevHelp failure.                 *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

unsigned near Build_UnitCBs()

{
  struct DevClassTableStruc far *pDriverTable;  /*  ptr to registered ADD EPs */

  NPIORB                npIORB;          /* far ptr to IORB                   */
  NPIORB_CONFIGURATION  npIORBDT;        /* far ptr to IORB                   */

  NPADAPTERINFO         npAdapterInfo;   /* near ptr to AdapterInfo           */

  void                  (far *DriverEP)(); /* Driver entry point              */
  void                  (far *DriverEPF)();/* Filter Driver entry point */ /*@V151168*/
  USHORT                FilterADDHandle;   /* Filter Handle             */ /*@V151168*/

  NPINITDATA            npInitData;      /* Pointer to init data              */

  NPUCB                 npUCB;

  NPQELE                npQEle;

  USHORT                rc;
  USHORT                iAdapter;
  USHORT                iDriver;
  USHORT                UnitIndex;
  USHORT                iUnit = 0;
  USHORT                i;

  USHORT                AdapterIndex = 0;                     /*@V128956*/
// @V175401  BOOL                  IncAdapterIndex = FALSE;              /*SM129585*/

  /*--------------------------------------------------------------------*/
  /* Get the adapter device tables for each adapter driver and create   */
  /* the unit control blocks (UnitCBs) from the returned tables.        */
  /*--------------------------------------------------------------------*/

  /*--------------------------------------*/
  /* Position INIT DATA after UNITCB pool */
  /*--------------------------------------*/

  npInitData = (NPINITDATA)((NPBYTE)Pool + (sizeof(UNITCB) * MAX_SCSI_DEVICES));

  npUCB      = UnitCB;

  rc = DevHelp_GetDOSVar((USHORT) DHGETDOSV_DEVICECLASSTABLE, 1,
                         (PPVOID) &pDriverTable);

  if (!rc)
  {
     /*----------------------------------------------------*/
     /* For each .ADD registered in the DEVICE CLASS TABLE */
     /*----------------------------------------------------*/

     NumDrivers = pDriverTable->DCCount;
     for (iDriver= 0; iDriver < NumDrivers; iDriver++)
     {
        npIORBDT = (NPIORB_CONFIGURATION)(npInitData->ScratchIORB);
        npIORB   = (NPIORB) npIORBDT;
        CLEAR_IORB_BUFF;

        npIORB->Length                 = sizeof(IORB_CONFIGURATION);
        npIORB->CommandCode            = IOCC_CONFIGURATION;
        npIORB->CommandModifier        = IOCM_GET_DEVICE_TABLE;

        npIORBDT->pDeviceTable         = (PDEVICETABLE)(npInitData->ScratchBuffer);
        npIORBDT->DeviceTableLen       = sizeof(npInitData->ScratchBuffer);

        OFFSETOF(DriverEP)   = pDriverTable->DCTableEntries[iDriver].DCOffset;
        SELECTOROF(DriverEP) = pDriverTable->DCTableEntries[iDriver].DCSelector;

        (*DriverEP)((PIORB_CONFIGURATION)npIORB);

        if (npIORB->Status & IORB_ERROR)
          continue;

        /*----------------------------------------*/
        /* For each adapter supported by this ADD */
        /*----------------------------------------*/

        for (iAdapter = 0;
             iAdapter < npIORBDT->pDeviceTable->TotalAdapters;
             iAdapter++                                  )
        {
           npAdapterInfo = npIORBDT->pDeviceTable->pAdapter[iAdapter];


           /*------------------------------*/
           /* For each Unit on the adapter */
           /*------------------------------*/

           for (UnitIndex = 0;
                UnitIndex < npAdapterInfo->AdapterUnits;
                UnitIndex++                              )
           {
              if (iUnit > MAX_SCSI_DEVICES )
                 continue;

              if (!(npAdapterInfo->UnitInfo[UnitIndex].UnitFlags & UF_NOSCSI_SUPT))
              {
// @V175401                 Index = TRUE;                          /*SM129585*/
                 npUCB->UnitInfo          = npAdapterInfo->UnitInfo[UnitIndex];

                 FilterADDHandle =                                              /*@V151168*/
                    npAdapterInfo->UnitInfo[UnitIndex].FilterADDHandle;          /*@V151168*/
                 if (FilterADDHandle == 0)                                      /*@V151168*/
                 {                                                              /*@V151168*/
                    npUCB->AdapterDriverEP   = DriverEP;                         /*@V151168*/
                 }                                                              /*@V151168*/
                 else                                                           /*@V151168*/
                 {                                                              /*@V151168*/
                    OFFSETOF(DriverEPF) =                                        /*@V151168*/
                       pDriverTable->DCTableEntries[FilterADDHandle-1].DCOffset;  /*@V151168*/
                    SELECTOROF(DriverEPF) =                                      /*@V151168*/
                       pDriverTable->DCTableEntries[FilterADDHandle-1].DCSelector;/*@V151168*/
                                                                                /*@V151168*/
                    OFFSETOF(npUCB->AdapterDriverEP)   = OFFSETOF(DriverEPF);    /*@V151168*/
                    SELECTOROF(npUCB->AdapterDriverEP) = SELECTOROF(DriverEPF);  /*@V151168*/
                 }                                                               /*@V151168*/

                 npUCB->UnitInfo.AdapterIndex = AdapterIndex;     /*@V128956*/
                 npUCB->Timeout               = DEFAULT_TIMEOUT;
                 npUCB->Index                 = iUnit;

                 if (npAdapterInfo->AdapterFlags & AF_IBM_SCB)
                    npUCB->IntUnitFlags    |= IUF_IBM_SCB;

                 npQEle = npUCB->TimeOutQFree = &npUCB->TimeOutQ[0];

                 for (i = 0; i < MAX_TIMEOUT_ENTRIES-1; i++, npQEle++ )
                 {
                    npQEle->Next = npQEle+1;
                 }

                 iUnit++;                /* Increment Index of UnitCB */
                 npUCB++;
              }

           }  /* end unit loop */
// @V175401
// @V175401           if (IncAdapterIndex)
// @V175401           {
// @V175401              IncAdapterIndex = FALSE;                            /*SM129585*/
              AdapterIndex++;    /* bump count of adapters */     /*@V128956*/
// @V175401           }
// @V175401
        }  /* end adapter loop */
     }  /* end driver loop */

                                         /* => just support MAX */
     return(iUnit);
  }
  else
     return(ERR_FNCALL);
}


#ifdef PARSE
/*
/***************************************************************************
 *
 * FUNCTION NAME = Command_Line_Parser
 *
 *
 * DESCRIPTION   =
 *
 *   Purpose:
 *
 *
 *   Function Calls:
 *
 *
 *
 *
 * INPUT         = (pCmdLine,pOptTable,pOutBuf,OutBuf_Len)
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/
/*
**
**  FUNCTION:  The Command_Line_Parser ADD help routine, assists ADDs with
**             parsing the CONFIG.SYS command line options. Using the
**             rules defined by the ADD supplied OPTIONTABLE, the command
**             parser:
**
**             1) Parses the CONFIG.SYS command line (pCmdLine).
**
**             2) Sets up the ADD supplied output buffer (pOutBuf) with
**                the parsed command line options.
**
**             3) Returns to the ADD with a completion code in the function
**                name.
**
**  SETUP:     To call the Command_Line_Parser routine, include the "CMDPHDR.H"
**             file in your calling module and  define the necessary parms by
**             using one of the supplied tables as a based. The supplied
**             tables reside in the following files:
**
**                CMDPDSKT.C/CMDPDSKT.H --> diskette type ADD parm definitions
**                CMDPDISK.C/CMDPDISK.H --> disk type ADD parm definitions
**                CMDPSCSI.C/CMDPSCSI.H --> SCSI type ADD parm definitions
**
**
**  PROTOTYPE:   CC  FAR Command_Line_Parser(PSZ pCmdLine, POPTIONTABLE pOptTable,
**                                      BYTE *pOutBuf, USHORT OutBuf_Len);
**
**  ENTRY:     pCmdLine     = Far pointer to an array of characters, which
**                            contains the CONFIG.SYS command line.
**
**             pOptTable    = Far pointer to the OPTIONTABLE structure, which
**                            contains the ADD defined rules for the
**                            Command_Line_Parser to follow.  It includes a list
**                            of the valid options and each for each option the
**                            command line syntax and output buffer format.
**
**              pOutBuf      = Pointer to an ADD supplied buffer (array of
**                            unsign characters), for the Command_Line_Parser
**                            to return the parsed command options and
**                            their associated values.
**
**             OutBuf_Len   = Size, in bytes of the ADD supplied output
**                            buffer (pOutBuf).  This value is relative to 1.
**
**
**  RETURN:    CC           = The function name contains the completion
**                            code information in the CC structure. Which
**                            consists of the following 2 fields:
**
**            ret_code = Completion Code
**
**                       Value                 Description
**                       -----------------     --------------------------------
**                       NO_ERR              - Successful
**
**                       SYNTAX_ERR          - Based on Option Table, type
**                                             field, a syntax error was found
**                                             parsing the value assigned to
**                                             option.
**
**                       BUF_TOO_SMALL_ERR   - OutBuf_Len value is too small.
**
**                       UNDEFINED_TYPE_ERR  - Option Table, type field is
**                                             undefined.
**
**                       UNDEFINED_STATE_ERR - Option Table state table field
**                                             contains an invalid state.
**
**                       NO_OPTIONS_FND_ERR  - No options as defined in the
**                                             Option Table were found.
**
**                       REQ_OPT_ERR         - Based on Option Table last entry
**                                             a required option was not found
**
**                       INVALID_OPT_ERR     - A / was found, but the option
**                                             was not in the Option Table
**
**
**              err_index = This field contains an index into the
**                          command line (pCmdLine) of the character
**                          being parsed when an error was detected.
**                          The index returned in this field is valid
**                          only when ret_code != NO_ERR.
**
**             pOutBuf      = The ADD supplied buffer pointed to by pOutBuf
**                            contains the successfully parsed command line
**                            options and their associated values, in the
**                            following format:
**
**
**                            -------------------------------------------------
**          OutBuf Format --> |option token| option token| ...| end of tokens |
**                            -------------------------------------------------
**                                 |                              |
**                                 |                              |
**                                 v                              v TOK_END
**                          ----------------------            ---------------
**  Option Token Format --> |length | id | value |            |  2    | -1  |
**                          ----------------------            ---------------
**                                                             length    id
**
**                            length    =  This field contains the total
**                                         length of the token, in bytes.
**
**                                         length (1) + id (1) + value
**
**                                         Note: The length of the value field
**                                               varies, based on the type
**                                               assigned to the option in
**                                               the OPTIONTABLE.
**
**                            id        =  This field contains the unique id
**                                         assigned to the option in the
**                                         OPTIONTABLE. opt_id = TOK_END,
**                                         denotes the end of the token string.
**
**                            value     =  This field contains the value
**                                         assigned to a option on the command
**                                         line.  The format and meaning
**                                         of this field varies based on the
**                                         type assigned to the option in
**                                         the OPTIONTABLE.
**
*/


USHORT NEAR Command_Line_Parser(PSZ pCmdLine, POPTIONTABLE pOptTable, PBYTE pOutBuf, USHORT OutBuf_Len)
{
   USHORT   i = 0;
   USHORT   j = 0;
   USHORT   end_index = 0;
   USHORT   ret_code = NO_ERR;
   USHORT   out_buf_index = 0;
   USHORT   state_index = 0;
   POPT     pend_option = NULL;
//   PSZ      pcmdline_slash = NULL;

   pcmd_line = pCmdLine;

   if (OutBuf_Len < (TOKLEN_ID_END + TOK_MIN_LENGTH))
   {
      return(BUF_TOO_SMALL_ERR);
   }

   for (i = 0; i < OutBuf_Len; i++)
   {
      pOutBuf[i] = 0;
   }

   Insert_End_Token(pOutBuf, out_buf_index);


   /*
   **  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 (state_index > pOptTable->max_states)
   {
      return(UNDEFINED_STATE_ERR);
   }


   /*
   **  On return from Locate_First_Slash call pcmdline_slash
   **  contains the ptr to the slash in the command line.
   */

   ret_code = Locate_First_Slash(&pcmdline_slash);
   if (ret_code != NO_ERR)
   {
      if (ret_code == NO_OPTIONS_FND_ERR)
      {
         if (pend_option->state[state_index] == R)
         {
            return(REQ_OPT_ERR);
         }
         else
         {
            return(NO_ERR);
         }
      }
      return(ret_code);
   }

   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)
      {
         POPT     ptable_option = pOptTable->poption[j];
         USHORT   option_length = strlen(ptable_option->string);

         /*
         **  Found a valid option. Check to see if this is the option
         **  entered at this point in command line.
         */

         if (strncmp(pcmdline_slash, ptable_option->string, option_length) == TRUE)
         {
            USHORT   tokv_index = 0;

            /*
            ** Found the command line option.  Now, syntax check its
            ** associated value.
            */

            pcmd_line = pcmdline_slash + option_length;

            ret_code = Parse_Option_Value(ptable_option, &tokv_index);
            if (ret_code != NO_ERR)
               return(ret_code);

            /*
            ** No syntax err detected.  Now, insert the option and its
            ** associated value into the output buffer in token format.
            */

            ret_code = Insert_Token(pOutBuf, &out_buf_index, OutBuf_Len, tokv_index, (BYTE) ptable_option->id);
            if (ret_code != NO_ERR)
               return(ret_code);

            /*
            **  Setup next index into the state table.
            */

            state_index = ptable_option->state[state_index];

            if (state_index > pOptTable->max_states)
            {
               return(UNDEFINED_STATE_ERR);
            }


            /*
            **  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.
            */

            ret_code = Locate_Next_Slash(&pcmdline_slash);
            if (ret_code != NO_ERR)
            {
               if (ret_code == NO_OPTIONS_FND_ERR)
               {
                  if (pend_option->state[state_index] == R)
                  {
                     return(REQ_OPT_ERR);
                  }
                  else
                  {
                     return(NO_ERR);
                  }
               }
               return(ret_code);
            }


            /*
            ** 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.
      */

      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.
      */

      ret_code = INVALID_OPT_ERR;
   }

   return(ret_code);
}


/***************************************************************************
 *
 * FUNCTION NAME = Locate_First_Slash
 *
 * DESCRIPTION   =
 *
 *   Purpose:  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.
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

/* static */ USHORT NEAR Locate_First_Slash(PSZ * ppcmdline_slash)
{

   while ((*pcmd_line != '\0') && (*pcmd_line != '\n') && (*pcmd_line != '\r'))
   {
      if (*pcmd_line == '/')
      {
         *ppcmdline_slash = pcmd_line;
         return(NO_ERR);
      }
      pcmd_line++;
   }

   return(NO_OPTIONS_FND_ERR);
}


/***************************************************************************
 *
 * FUNCTION NAME = strlen
 *
 * DESCRIPTION   =
 *
 *   Purpose: Return the length of a string
 *
 *   Function Calls:
 *
 * INPUT         = (s)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR strlen(const CHAR FAR *s)
{
   USHORT i;

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

   return(i);
}


/***************************************************************************
 *
 * FUNCTION NAME = strncmp
 *
 * DESCRIPTION   =
 *
 *   Purpose: Compare n number of characters in 2 strings, return TRUE if =
 *            If s1 is in lower case, convert to upper prior to comparing.
 *
 *   Function Calls:
 *
 * INPUT         = (s1,s2,n)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ BOOL NEAR strncmp(const CHAR FAR *s1, const CHAR FAR *s2, USHORT n)
{
  USHORT 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 NAME = Parse_Option_Value
 *
 * DESCRIPTION   =
 *
 *   Purpose: Parse the command line for the value assigned to located option
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR Parse_Option_Value(POPT ptable_option, PUSHORT ptokv_index)
{
   USHORT   ret_code = NO_ERR;
   USHORT   t;


   Skip_Over_Blanks();

   for (t = 0; t < TOKVBUF_LEN; t++)
   {
      tokvbuf[t] = 0;
   }

   *ptokv_index = 0;

  switch (ptable_option->type)
  {
    case  TYPE_0 :
      break;
#if 0
    case  TYPE_CHAR :
      char_parser(ptokv_index);
      break;
    case  TYPE_D :
      ret_code = d_parser(ptokv_index);
      break;
    case  TYPE_DD :
      ret_code = dd_parser(ptokv_index);
      break;
    case  TYPE_HH :
      ret_code = hh_parser(ptokv_index);
      break;
    case  TYPE_HHHH :
      ret_code = hhhh_parser(ptokv_index);
      break;
#endif
    default  :
      ret_code = UNDEFINED_TYPE_ERR;
  }                                    /* endswitch                          */

   return(ret_code);
}


/***************************************************************************
 *
 * FUNCTION NAME = Skip_Over_Blanks
 *
 * DESCRIPTION   =
 *
 *   Purpose:  Skip over all the blank and tab characters
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ VOID NEAR Skip_Over_Blanks()
{
  while ((*pcmd_line == ' ') || (*pcmd_line == '\t'))
    pcmd_line++;
  return;
}

#if 0
/***************************************************************************
 *
 * FUNCTION NAME = char_parser
 *
 * DESCRIPTION   =
 *
 *   Purpose: TYPE_CHAR option parser - scan till blank,tab,cr,new line or
 *                                      end of string char
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ VOID NEAR char_parser(PUSHORT ptokv_index)
{
  while ((*pcmd_line != '\0') && (*pcmd_line != '\n') && (*pcmd_line != '\r') && (*pcmd_line != '/'))
  {
    tokvbuf[*ptokv_index] = *pcmd_line;
    (*ptokv_index)++;
    pcmd_line++;
  }
  return;
}


/***************************************************************************
 *
 * FUNCTION NAME = d_parser
 *
 * DESCRIPTION   =
 *
 *   Purpose: TYPE_D option parser - one digit decimal number (d)
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR d_parser(PUSHORT ptokv_index)
{
  if ((*pcmd_line >= '0') && (*pcmd_line <= '9'))
  {
      tokvbuf[*ptokv_index] = (*pcmd_line) - '0';
      (*ptokv_index)++;
      pcmd_line++;

      return(NO_ERR);
  }
  else
  {
      return(SYNTAX_ERR);
  }
}


/***************************************************************************
 *
 * FUNCTION NAME = dd_parser
 *
 * DESCRIPTION   =
 *
 *   Purpose: TYPE_DD option parser - two digit decimal number (dd)
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR dd_parser(PUSHORT ptokv_index)
{
   USHORT   i;
   BYTE     n = 0;
   BOOL     flag = FALSE;

  for (i = 0; i < 2; i++)
  {
    if ((*pcmd_line >= '0') && (*pcmd_line <= '9'))
    {
      n = 10*n + (*pcmd_line) - '0';
      pcmd_line++;
      flag = TRUE;
    }
    else
    {

      /*
      ** Was at least 1 digit found on the command line?
      */

      if (flag)
        break;
      return(SYNTAX_ERR);
    }
  }
  tokvbuf[*ptokv_index] = n;
  (*ptokv_index)++;
  return(NO_ERR);
}


/***************************************************************************
 *
 * FUNCTION NAME = hh_parser
 *
 * DESCRIPTION   =
 *
 *   Purpose: TYPE_HH option parser        hh,hh format (h = hex char)
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR hh_parser(PUSHORT ptokv_index)
{
   USHORT   ret_code;

  /*
  **  Convert command line HH char and setup token value buffer
  */

   ret_code = HH_Char_To_Byte(ptokv_index);
   if (ret_code != NO_ERR)
      return(ret_code);

  Skip_Over_Blanks();
  if (*pcmd_line != ',')
  {
    return(SYNTAX_ERR);
  }
  pcmd_line++;
  Skip_Over_Blanks();

  /*
  **  Convert command line HH char and setup token value buffer
  */

  ret_code = HH_Char_To_Byte(ptokv_index);
  return(ret_code);
}


/***************************************************************************
 *
 * FUNCTION NAME = HH_Char_To_Byte
 *
 * DESCRIPTION   =
 *
 *   Purpose: Convert HH char to byte value
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR HH_Char_To_Byte(PUSHORT ptokv_index)
{
  BYTE   n;
  USHORT i;
  BOOL flag;
  n = 0;
  flag = FALSE;

  for (i = 0; i < 2; i++)
  {
    if ((*pcmd_line >= '0') && (*pcmd_line <= '9'))
    {
      n = 16*n + *pcmd_line - '0';
      pcmd_line++;
      flag = TRUE;
      continue;
    }
    if ((*pcmd_line >= 'A') && (*pcmd_line <= 'F'))
    {
      n = 16*n + *pcmd_line - 'A' + 10;
      pcmd_line++;
      flag = TRUE;
      continue;
    }
    if ((*pcmd_line >= 'a') && (*pcmd_line <= 'f'))
    {
      n = 16*n + *pcmd_line - 'a' + 10;
      pcmd_line++;
      flag = TRUE;
      continue;
    }

    /*
    ** Was at least 1 hex digit found on the command line?
    */

    if (flag)
      break;
    return(SYNTAX_ERR);
  }

  tokvbuf[*ptokv_index] = n;
  (*ptokv_index)++;

  return(NO_ERR);
}


/***************************************************************************
 *
 * FUNCTION NAME = hhhh_parser
 *
 * DESCRIPTION   =
 *
 *   Purpose: TYPE_HHHH option parser       hhhh format (h = hex char)
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR hhhh_parser(PUSHORT ptokv_index)
{
  USHORT i;
  BOOL flag;
  NUMBER un_number;
  un_number.n = 0;
  flag = FALSE;

  for (i = 0; i < 4; i++)
  {
    if ((*pcmd_line >= '0') && (*pcmd_line <= '9'))
    {
      un_number.n = 16*un_number.n + *pcmd_line - '0';
      pcmd_line++;
      flag = TRUE;
      continue;
    }
    if ((*pcmd_line >= 'A') && (*pcmd_line <= 'F'))
    {
      un_number.n = 16*un_number.n + *pcmd_line - 'A' + 10;
      pcmd_line++;
      flag = TRUE;
      continue;
    }
    if ((*pcmd_line >= 'a') && (*pcmd_line <= 'f'))
    {
      un_number.n = 16*un_number.n + *pcmd_line - 'a' + 10;
      pcmd_line++;
      flag = TRUE;
      continue;
    }

    /*
    ** Was at least 1 hex digit found on the command line?
    */

    if (flag)
      break;
    return(SYNTAX_ERR);
  }

  tokvbuf[*ptokv_index] = un_number.two_bytes.byte1;
  (*ptokv_index)++;

  tokvbuf[*ptokv_index] = un_number.two_bytes.byte2;
  (*ptokv_index)++;

  return(NO_ERR);
}


/***************************************************************************
 *
 * FUNCTION NAME = scsi_id_parser
 *
 * DESCRIPTION   =
 *
 *   Purpose: TYPE_SCSI_ID option parser - format d and (d,d) accepted
 *                                         where d = 0 - 7
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ VOID NEAR scsi_id_parser(PUSHORT ptokv_index)
{
  BOOL found_bracket,found_one;
  found_bracket = FALSE;
  found_one = FALSE;
  if (*pcmd_line == '(' )
  {
    found_bracket = TRUE;
    pcmd_line++;
    Skip_Over_Blanks();
  }
  while ((*pcmd_line >= '0') && (*pcmd_line <= '7'))
  {
    found_one = TRUE;

    tokvbuf[*ptokv_index] = *pcmd_line - '0';
    (*ptokv_index)++;
    pcmd_line++;

    if (!found_bracket)
    {

      tokvbuf[*ptokv_index] = 0;
      (*ptokv_index)++;
    }
    Skip_Over_Blanks();
    if (*pcmd_line != ',')
    {
      if (found_bracket)
        ret_code = SYNTAX_ERR;
      return ;
    }
    pcmd_line++;
    Skip_Over_Blanks();
    if (found_bracket)
    {
      if ((*pcmd_line >= '0') && (*pcmd_line <= '7'))
      {
        tokvbuf[*ptokv_index] = *pcmd_line - '0';
        (*ptokv_index)++;
        pcmd_line++;
        Skip_Over_Blanks();
        if (*pcmd_line != ')')
        {
          ret_code = SYNTAX_ERR;
          return ;
        }
        pcmd_line++;
        Skip_Over_Blanks();
        found_bracket = FALSE;
        if (*pcmd_line != ',')
          return ;
        pcmd_line++;
        Skip_Over_Blanks();
      }
    }
    if (*pcmd_line == '(')
    {
      found_bracket = TRUE;
      pcmd_line++;
      Skip_Over_Blanks();
    }
  }
  if (found_bracket)
    ret_code = SYNTAX_ERR;
  if (!found_one)
    ret_code = SYNTAX_ERR;
  return ;
}


#endif


/***************************************************************************
 *
 * FUNCTION NAME = Insert_Token
 *
 * DESCRIPTION   =
 *
 *   Purpose: Insert the parsed option (token) into the output buffer.
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

/* static */ USHORT NEAR Insert_Token(PBYTE pOutBuf, PUSHORT pout_buf_index, USHORT out_buf_len,
                                USHORT tokv_index, BYTE option_id)
{
   USHORT   tok_size = tokv_index + TOK_MIN_LENGTH;
   USHORT   t;

   if ( (*pout_buf_index + tok_size + TOKLEN_ID_END) >= out_buf_len)
   {
      return(BUF_TOO_SMALL_ERR);
   }

   pOutBuf[*pout_buf_index] = tok_size;
   (*pout_buf_index)++;

   pOutBuf[*pout_buf_index] = option_id;
   (*pout_buf_index)++;


   for (t = 0; t < tokv_index; t++)
   {
      pOutBuf[*pout_buf_index] = tokvbuf[t];
      (*pout_buf_index)++;
   }


   Insert_End_Token(pOutBuf, *pout_buf_index);

   return(NO_ERR);
}


/***************************************************************************
 *
 * FUNCTION NAME = Insert_End_Token
 *
 * DESCRIPTION   =
 *
 *   Purpose: Insert the end of token marker into the output buffer
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

/* static */ VOID NEAR Insert_End_Token(PBYTE pOutBuf, USHORT out_buf_index)
{
   // insert end token length and ID, but don't change index
   // these values may get overwritten by another token

   pOutBuf[out_buf_index] = TOKLEN_ID_END;
   pOutBuf[out_buf_index + 1] = TOK_ID_END;

   return;
}


/***************************************************************************
 *
 * FUNCTION NAME = Locate_Next_Slash
 *
 * DESCRIPTION   =
 *
 *   Purpose: Locate the next / char.
 *
 *   Function Calls:
 *
 * INPUT         = ()
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

/* static */ USHORT NEAR Locate_Next_Slash(PSZ * ppcmdline_slash)
{
  while ((*pcmd_line != '\0') && (*pcmd_line != '\n') && (*pcmd_line != '\r'))
  {
      if ((*pcmd_line == ' ') || (*pcmd_line == '\t'))
      {
         pcmd_line++;
      }
      else
      {
         if (*pcmd_line == '/')
         {
            *ppcmdline_slash = pcmd_line;
            return(NO_ERR);
         }
         else
         {
            return(INVALID_OPT_ERR);
         }
      }
   }                                    /* endwhile                           */

   return(NO_OPTIONS_FND_ERR);
}

#endif

