/**************************************************************************
 *
 * SOURCE FILE NAME = PINIFILE.C
 *
 * DESCRIPTIVE NAME = Process init file (TOUCH.INI) & update global variables
 *
 * Copyright : COPYRIGHT IBM CORPORATION, 1991, 1992
 *             LICENSED MATERIAL - PROGRAM PROPERTY OF IBM
 *             REFER TO COPYRIGHT INSTRUCTION FORM#G120-2083
 *             RESTRICTED MATERIALS OF IBM
 *             IBM CONFIDENTIAL
 *
 * VERSION = V2.0
 *
 * DATE        08/08/91
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS      process_init_file,   GetArgs,       FindToken,
 *                ReadLine,            GetPutMsg.
 *
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 * CHANGE ACTIVITY =
 *  DATE      FLAG        APAR   CHANGE DESCRIPTION
 *  --------  ----------  -----  --------------------------------------
 *  mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
 *  11/23/92                     Added support to ignore entry of form
 *                               '[touch]' required for Win 3.1 support,
 *                               which also parses the touch.ini file. -- drw
 ****************************************************************************/

#define  INCL_DOS
#define  INCL_KBD
#include <os2.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "tddcalls.h"

/*
** Typedefs & Defines
*/
#define  HKBD                 0       /* kbd handle                          */
#define  MSG_PRESS_RET        1726
#define  NO_ERROR             0
#define  ERR_EOF              -1

/*
** These error numbers match the message numbers in tdd.msg
*/
#define  INF_DD_SIGNON        12
#define  ERR_FILE_NOT_FOUND   37
#define  ERR_SYNTAX           38
#define  ERR_GENERAL          39
#define  ERR_DEFAULTS         40
#define  ERR_BAD_ARGS         41
#define  ERR_DUP_TOK          42

typedef enum _ETOK
{
  TOK_THRE = 0, TOK_CLICK, TOK_XYOFF, TOK_RATE, TOK_EMUL, TOK_BEEP, TOK_FILT,
     TOK_MPI, TOK_SECTION,            /* @drw 11/23                          */
     TOK_ILLEGAL = -1
} ETOK;

typedef struct _TOK
{
  CHAR    *name;                      /* token name                          */
  ETOK    idx;                        /* token index                         */
  USHORT  nargs;                      /* number of arguments                 */
  UCHAR   fUsed;                      /* used flag                           */
} TOKENS, *PTOKENS;

/*
** Prototypes
*/
USHORT ReadLine(PCHAR Buff,USHORT BuffSize,PUSHORT pLineNum,HFILE Handle);
USHORT GetArgs(PCHAR ArgPtr,USHORT NumArgs,PUSHORT Args,USHORT MaxArgs);
SHORT  FindToken(PCHAR Line,PTOKENS pTokens,PCHAR *pArg);
VOID NEAR _cdecl GetPutMsg(PCHAR msg_file,USHORT msg_num,USHORT num_parms,...);

/*
** Externals
*/
extern USHORT thre_oth_z;
extern USHORT thre_phy_z;
extern USHORT thre_pth_z;
extern USHORT clk_type;
extern USHORT clk_size;
extern USHORT clk_time;
extern USHORT x_offset;
extern USHORT y_offset;
extern USHORT report_rate;
extern USHORT def_emul;
extern USHORT def_beep;
extern TDDFILTCONST filter_parms;
extern USHORT glass_mpi;
extern USHORT banner_done;

/*
** Globals
*/
static TOKENS Tokens[] =
{
  /* Token        Token           Num     Used  */
  /* name         index           parms   flag  */

  "THRESH=",      TOK_THRE,       3,      0,
  "CLICK=",       TOK_CLICK,      3,      0,
  "XYOFF=",       TOK_XYOFF,      2,      0,
  "RATE=",        TOK_RATE,       1,      0,
  "EMUL=",        TOK_EMUL,       1,      0,
  "BEEP=",        TOK_BEEP,       1,      0,
  "FILT=",        TOK_FILT,       2,      0,
  "MPI=",         TOK_MPI,        1,      0,
  "[TOUCH]",      TOK_SECTION,    0,      0,      /* @drw 11/23              */
  NULL,           0,              0,      0
} ;

#define  MAX_ARGS             3       /* maximum number of args used, above  */

CHAR TDD_MSG[] = "TDD.MSG";
CHAR SYS_MSG[] = "OSO001.MSG";


/****************************************************************************
 *
 * FUNCTION NAME = process_init_file
 *
 * DESCRIPTION   = Extract parameters from initialisation file
 *
 *
 * INPUT         = (PCHAR fname)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

USHORT FAR PASCAL process_init_file(PCHAR fname)
{
  USHORT ret;                         /* gp ret val                          */
  CHAR fbuff[1024];                   /* here as can't use DosAllocSeg??     */
  USHORT action;                      /* open action taken                   */
  CHAR lbuff[256];                    /* max line len including terminator   */
  HFILE Handle = 0;                   /* ini file handle                     */
  PCHAR arg_ptr;                      /* ptr to argument after token         */
  USHORT path_len;                    /* max path length                     */
  USHORT LineNumber;                  /* error line number                   */
  USHORT args[MAX_ARGS];              /* array of extracted arguments        */
  USHORT idx;                         /* token index                         */
  CHAR MsgLineNum[] = "65535";        /* error line number                   */
  USHORT pause_required = 0;          /*                                     */
  CHAR OpBuff;                        /*                                     */
  STRINGINBUF StrInBuf = { 1,0 } ;    /*                                     */

  /* ??? could put file name into messages   */
  /* See if local file buffer big enough!    */
  if (DosQSysInfo(Q_MAX_PATH_LENGTH, &path_len, sizeof(path_len)) ||
      sizeof(fbuff) < path_len)
  {                                /* dos call failed or buffer too small!   */
    ret = ERR_GENERAL;
    goto pif_exit;
  }

  /* Search for file in DPATH */
  if (DosSearchPath(2, "DPATH", fname, fbuff, sizeof(fbuff)))
  {                           /* file not found, use file name as supplied   */
    _fstrcpy(fbuff, fname);           /* use supplied file name              */
  }

  if (DosOpen(fbuff,                  /* file name                           */
     &Handle,                         /* file handle                         */
     &action,                         /* action taken                        */
     0l,                              /* file size                           */
     FILE_NORMAL,                     /* file attributes                     */
     FILE_OPEN,                       /* open flag                           */
     OPEN_ACCESS_READWRITE |
       OPEN_FLAGS_FAIL_ON_ERROR |
       OPEN_FLAGS_NO_LOCALITY |
       OPEN_FLAGS_NOINHERIT |
       OPEN_SHARE_DENYNONE,           /* open mode                           */
     0l                               /* reserved                            */
     ))
  {
    ret = ERR_FILE_NOT_FOUND;
    goto pif_exit;
  }

  LineNumber = 0;                     /* initialise                          */
  while (!(ret = ReadLine(lbuff, sizeof(lbuff), &LineNumber, Handle)))
  {                                   /* while not error or eof              */
    itoa(LineNumber, MsgLineNum, 10);

    if ((idx = FindToken(lbuff, Tokens, &arg_ptr)) == TOK_ILLEGAL ||
        GetArgs(arg_ptr, Tokens[idx].nargs, args, sizeof(args)/sizeof(args[0])))
    {                  /* token not found or number of arguments incorrect   */
      GetPutMsg(TDD_MSG, ERR_SYNTAX, 2, fname, MsgLineNum);
      pause_required = 1;
      continue;                       /* skip to next token                  */
    }

    if (Tokens[idx].fUsed)
    {                                 /* token already found in ini file     */
      GetPutMsg(TDD_MSG, ERR_DUP_TOK, 2, fname, MsgLineNum);
      pause_required = 1;
      continue;                       /* skip to next token                  */
    }

    switch (Tokens[idx].idx)
    {                     /* check supplied arguments & update global vars   */
      case  TOK_THRE :                /* thresholds                          */
        /*
        ** Good parms if:
        **      on-screen < push-hys < push-thr
        **      push-hys - on-screen > 1
        **      on-screen > 3
        */
        if (args[0] <= 3 || args[0] >= args[1]-1 ||
            args[1] >= args[2] || args[2] >= 256)
          break;                      /* parms bad                           */
        thre_oth_z = args[0];
        thre_phy_z = args[1];
        thre_pth_z = args[2];
        Tokens[idx].fUsed = 1;
        continue;                     /* parms ok                            */

      case  TOK_CLICK :               /* click locking                       */
        if (args[0] > 2 || args[1] > 4095 || args[2] == 0)
          break;                      /* parms bad                           */
        clk_type = (UCHAR)args[0];
        clk_size = args[1];
        clk_time = args[2];
        Tokens[idx].fUsed = 1;
        continue;                     /* parms ok                            */

      case  TOK_XYOFF :               /* xy offset (+/-)                     */
        if ((SHORT)args[0] > 4095 || (SHORT)args[0] < -4095 ||
            (SHORT)args[1] > 4095 || (SHORT)args[1] < -4095)
          break;                      /* parms bad                           */
        x_offset = args[0];
        y_offset = args[1];
        Tokens[idx].fUsed = 1;
        continue;                     /* parms ok                            */

      case  TOK_RATE :                /* report rate                         */
        if (args[0] > 4)
          break;                      /* parms bad                           */
        report_rate = args[0];
        Tokens[idx].fUsed = 1;
        continue;                     /* parms ok                            */

      case  TOK_EMUL :                /* emulation                           */
        if (args[0] > 1)
          break;                      /* parms bad                           */
        def_emul = args[0];
        Tokens[idx].fUsed = 1;
        continue;                     /* parms ok                            */

      case  TOK_BEEP :                /* beeper                              */
        if (args[0] > 1)
          break;                      /* parms bad                           */
        def_beep = args[0];
        Tokens[idx].fUsed = 1;
        continue;

      case  TOK_FILT :                /* filters                             */
        if (args[0] > 1 || args[1] > 4)
          break;                      /* parms bad                           */
        filter_parms.type = args[0];
        filter_parms.freq = args[1];
        Tokens[idx].fUsed = 1;
        continue;

      case  TOK_MPI :                 /* mickeys per inch                    */
        if (args[0] > 4095)
          break;                      /* parms bad                           */
        glass_mpi = args[0];
        Tokens[idx].fUsed = 1;
        continue;

      case  TOK_SECTION :             /* section header @drw 11/23           */
        continue;                     /* do nothing, discard it @drw 11/23   */
    }                                 /* switch                              */

    /* Come here if args bad, from a 'break' in switch above                 */
    GetPutMsg(TDD_MSG, ERR_BAD_ARGS, 2, fname, MsgLineNum);
    pause_required = 1;
  }                                   /* while                               */

pif_exit:                             /* come here on fatal error            */
  itoa(LineNumber, MsgLineNum, 10);
  if (ret != ERR_EOF)
  {
    GetPutMsg(TDD_MSG, ret, 2, fname, MsgLineNum);
    GetPutMsg(TDD_MSG, ERR_DEFAULTS, 0);
    pause_required = 1;
  }

  if (pause_required)
  {                  /* wait for a <CR/LF> as there has been a warning msg   */
    KbdFlushBuffer(HKBD);
    GetPutMsg(SYS_MSG, MSG_PRESS_RET, 0);
    KbdStringIn(&OpBuff, &StrInBuf, IO_WAIT, HKBD);
  }

  if (Handle)
    DosClose(Handle);

  return (NO_ERROR);                  /* return any errors                   */
}                                     /* process_init_file                   */


/****************************************************************************
 *
 * FUNCTION NAME = GetArgs
 *
 * DESCRIPTION   = Extract args sepparated by commas
 *
 *
 * INPUT         = (PCHAR ArgPtr,USHORT NumArgs,PUSHORT Args,USHORT MaxArgs)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

USHORT GetArgs(PCHAR ArgPtr, USHORT NumArgs, PUSHORT Args, USHORT MaxArgs)
{
  PCHAR currArg;

  if (NumArgs > MaxArgs)
    return (1);                      /* not enough space for all arguments   */

  for (currArg = _fstrtok(ArgPtr, ","); NumArgs != 0 && currArg != NULL;
       currArg = _fstrtok(NULL, ","), --NumArgs)
  {
    *Args++ = atoi(currArg);
  }

  if (NumArgs != 0 || currArg != NULL)
    return (1);                       /* incorrect number of args supplied   */

  return (0);                         /* no error                            */
}                                     /* GetArgs                             */


/****************************************************************************
 *
 * FUNCTION NAME = FindToken
 *
 * DESCRIPTION   = Search list of tokens for a match
 *
 *
 * INPUT         = (PCHAR Line,PTOKENS pTokens,PCHAR *pArg)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

SHORT FindToken(PCHAR Line, PTOKENS pTokens, PCHAR *pArg)
{
  SHORT idx;                          /* index into token array              */
  PCHAR pTok;                         /* ptr to current token                */
  USHORT lenTok;                      /* length of current token             */

  for (idx = 0; (pTok = pTokens[idx].name); ++idx)
  {                                  /* search list of tokens, ignore case   */
    lenTok = _fstrlen(pTok);
    if (!_fstrnicmp(pTok, Line, lenTok))
      break;
  }                                   /* for                                 */

  if (pTok)
  {                                   /* token found                         */
    *pArg = &Line[lenTok];            /* start of args after token           */
    return (idx);
  }
  else
    return (TOK_ILLEGAL);             /* not found                           */
}                                     /* FindToken                           */


/****************************************************************************
 *
 * FUNCTION NAME = ReadLine
 *
 * DESCRIPTION   = Reads up to <LF> or <EOF> ignores white spaces & comments,
 *                 two types: '/*' '//' writes to Buff of max length BuffSize
 *                 & terminates it.
 *
 *
 * INPUT         = (PCHAR Buff,USHORT BuffSize,PUSHORT pLineNum,HFILE Handle)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

USHORT ReadLine(PCHAR Buff, USHORT BuffSize, PUSHORT pLineNum, HFILE Handle)
{
  static USHORT fComment = 0;         /* flag set if open '/*' comment       */
  USHORT cbRead;                      /* bytes read                          */
  CHAR currC;                         /* current character                   */
  CHAR lastC = 0;                     /* last character read                 */
  USHORT fIgnore;                     /* flag to ignore till eol             */
  PCHAR lBuff;                        /* line buffer ptr                     */
  USHORT lBuffSize;                   /* line buffer remaining               */

  --BuffSize;                /* reduce buffer size to allow for terminator   */

  do
  {                        /* read till non-empty & non-comment line found   */
    /* New line so ... */
    lBuff = Buff;                     /* reset line buffer start             */
    lBuffSize = BuffSize;             /* reset line buffer size              */
    fIgnore = 0;                      /* reset ignore flag                   */
    if (pLineNum != NULL)
      ++*pLineNum;                    /* bump line number if ptr supplied    */

    do
    {                  /* read one character at a time until <LF> or <EOF>   */
      if (DosRead(Handle, &currC, 1, &cbRead))
        return (ERR_GENERAL);
      if (cbRead == 0)
        break;                        /* eof                                 */
      if (!fComment && !fIgnore)
      {                               /* comment not active                  */
        if (lastC == '/')
        {
          if (currC == '/')
          {
            --lBuff;                  /* remove previous slash               */
            ++lBuffSize;
            fIgnore = 1;              /* ignore rest of line                 */
            continue;                 /* do not save current char            */
          }
          else
            if (currC == '*')
            {
              --lBuff;                /* remove previous slash               */
              ++lBuffSize;
              fComment = 1;           /* comment now active                  */
              continue;               /* do not save current char            */
            }
        }
        switch (currC)
        {                             /* ignore some characters              */
          case '\t' :                 /* white spaces                        */
          case ' ' :
          case '\n' :
          case '\r' :
          case '\x1a' :               /* eof mark                            */
            /* do not save */
            break;

          default  :                  /* save character if none of above     */
            *lBuff++ = currC;
            --lBuffSize;
            break;
        }                             /* switch - ignore                     */
      }
      else
      {                         /* comment active or ignore to end of line   */
        /*
        ** The following code resets an open '**' comment.
        ** It could be executed after a '//' was found
        ** if that line contained a '**', but as fComment
        ** is already reset in those circumstances, it will
        ** have no effect.
        */
        if (lastC == '*' && currC == '/')
          fComment = 0;               /* comment now not active              */
      }

      /*
      ** *** NB. Will not cope with line that ends in <CR> ***
      */
    }  while ((lastC = currC) != '\n' && lBuffSize);

    if (lBuffSize == 0)
      return (ERR_GENERAL);           /* error: buffer full                  */

    /* Test if: not eof && line empty */
  } while (cbRead != 0 && lBuff == Buff);/* blank or commented line          */

  *lBuff = '\0';                      /* terminate buffer at current position*/
  if (cbRead == 0 && lBuff == Buff)
  {                                   /* eof & line empty                    */
    if (fComment)
      return (ERR_SYNTAX);            /* error: open comment at eof          */
    else
      return (ERR_EOF);
  }

  return (NO_ERROR);
}                                     /* ReadLine                            */


/****************************************************************************
 *
 * FUNCTION NAME = GetPutMsg
 *
 * DESCRIPTION   = Get & put a message with parameter substitution
 *
 *
 * INPUT         = (PCHAR msg_file,USHORT msg_num,USHORT num_parms,...)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID NEAR _cdecl GetPutMsg(PCHAR msg_file, USHORT msg_num, USHORT num_parms,...)
{
  va_list arg_ptr;
  PCHAR parms_table[9];
  CHAR data_area[160];
  USHORT msg_len;
  USHORT ret;
  USHORT parms_idx;

#define  STDOUT      1

  va_start(arg_ptr, num_parms);

  /* build parameter list array */
  for (parms_idx = 0;
       parms_idx < num_parms && parms_idx < sizeof(parms_table)/sizeof(PCHAR);
       ++parms_idx)
    parms_table[parms_idx] = va_arg(arg_ptr, PCHAR);

  if (!banner_done)
  {                                   /* drw 1/8/91                          */
    ret = DosGetMessage(parms_table, num_parms, (PCHAR)data_area,
                        sizeof(data_area), INF_DD_SIGNON,
                        (PCHAR)msg_file, (PUSHORT)&msg_len);

    ret = DosPutMessage(STDOUT, msg_len, (PCHAR)data_area);
    banner_done = 1;
  }

  ret = DosGetMessage(parms_table, num_parms, (PCHAR)data_area,
                      sizeof(data_area), msg_num, (PCHAR)msg_file,
                      (PUSHORT)&msg_len);

  ret = DosPutMessage(STDOUT, msg_len, (PCHAR)data_area);
  va_end(arg_ptr);
}                                     /* GetPutMsg                           */

/*
** end
*/
