/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*              1991-2006, All Rights Reserved       			*/
/*									*/
/* MODULE NAME : cfg_util.c						*/
/* PRODUCT(S)  : 							*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 02/07/06  EJV     18    cfg_special_char_str: added '$'.		*/
/* 02/20/03  JRB     17    Del PSOS code.				*/
/* 11/29/01  EJV     16    Del code for old LATT, XENIX, ultrix, RMX86.	*/
/*			   Removed call to strcasecmp (sun)		*/
/* 10/18/01  JRB     15    Add str_util.h (for strcmpi, etc. protos)	*/
/* 01/19/01  EJV     14    Moved strcmpi,stricmp,strnicmp to str_util.c	*/
/* 11/07/00  JRB     13    Del QNX from ifdef. It supports stricmp, etc.*/
/* 04/28/00  JRB     12    Lint cleanup					*/
/* 09/13/99  MDE     11    Added SD_CONST modifiers			*/
/* 04/14/99  MDE     10    Replaced SYSTEM_SEL with compiler defines	*/
/* 10/08/98  MDE     09    Migrated to updated SLOG interface		*/
/* 08/24/98  EJV     08    Added __hpux to str* cmp functions		*/
/* 08/06/98  JRB     07    Added cfg_get_octet_string function.		*/
/* 06/15/98  MDE     06    Changes to allow compile under C++		*/
/* 05/22/98  EJV     05    added _AIX, sun, and __alpha to		*/
/*			     stricmp, strcmpi, strnicmp functions	*/
/* 04/03/98  RKR     04    added stricmp, strcmpi, strnicmp		*/
/* 12/22/97  JRB     03    Clean up PSOS code.				*/
/* 12/04/97  KCR     02    Added cfg_goto_keyword function        	*/
/* 11/05/97  MDE     01    Added VXWORKS support            		*/
/* 04/02/97  DTL   7.00    MMSEASE 7.0 release. See MODL70.DOC for	*/
/*			   history.					*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include "mem_chk.h"
#include "cfg_util.h"
#include "cfglog.h"
#include "str_util.h"
#include <ctype.h>

#include "slog.h"

/************************************************************************/

/* #define DEBUG */

/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

/************************************************************************/

/* Externally visable variables                                         */
ST_CHAR *cfg_line_buf;             	/* File line read buffer        */
ST_INT cfg_curr_line;    		/* Current line number          */
ST_INT cfg_line_offset;	        	/* Offset into cfg_line_buf     */

ST_CHAR *cfg_special_char_str;
ST_CHAR *cfg_end_of_rval_str;

ST_BOOLEAN config_stop;
ST_RET config_err;
ST_BOOLEAN config_eof;

ST_UINT cfg_log_mask;
#ifdef DEBUG_SISCO 
SD_CONST ST_CHAR *SD_CONST _cfg_err_logstr = "CFG_LOG_ERR";
SD_CONST ST_CHAR *SD_CONST _cfg_flow_logstr = "CFG_LOG_FLOW";
#endif

/************************************************************************/

/* Internal definitions                                                 */
#define MAX_STRING_LEN 		256
#define LINE_BUF_SIZE		256

ST_INT cfg_max_string_len;
ST_INT cfg_line_buf_size;
ST_INT cfg_max_rval_len;

#define MAX_CONFIG_NEST		 10

/* Internal variables                                                   */
static ST_CHAR *cfg_string_buf;
static struct cfg_kw_el *curr_table;
static ST_BOOLEAN cfg_need_new_line;	
static FILE *config_fptr;		/* Configuration file handle            */

/* Keyword table stack control                                          */
static ST_INT num_on_stack;
static struct cfg_kw_el **cfg_kw_stack;


/* Internal funtions                                                    */
static struct cfg_kw_el *cfg_lookup_keyword (struct cfg_kw_el *, ST_CHAR *);
static ST_RET cfg_get_next_keyword (FILE *);
static ST_CHAR *cfg_get_rval (ST_VOID);
static ST_RET cfg_end_of_rval (ST_CHAR c);
static ST_RET cfg_special_char (ST_CHAR c);

/************************************************************************/
/************************************************************************/
/************************************************************************/
/*                       cfg_process_file				*/
/************************************************************************/

ST_RET cfg_process_file (ST_CHAR *fileName, struct cfg_kw_el *root_kw_tbl)
  {
ST_RET ret;

  if (cfg_special_char_str == NULL)
    cfg_special_char_str = ":\\/-_ .{}[]<>@,()$";

  if (cfg_end_of_rval_str == NULL)
    cfg_end_of_rval_str = "|";

  if (cfg_max_string_len == 0)
    cfg_max_string_len = MAX_STRING_LEN;

  if (cfg_line_buf_size  == 0)
    cfg_line_buf_size  = LINE_BUF_SIZE;	

  if (cfg_max_rval_len == 0)
    cfg_max_rval_len = MAX_RVAL_LEN;

/* Open the configuration file                                          */
  if ((config_fptr = fopen (fileName,"r"))==NULL)
    {
    CFG_LOG_ERR1 ("Config File (%s) Open Error",fileName);
    return (SD_FAILURE);
    }

  CFG_LOG_FLOW1 ("Config File (%s) Opened OK",fileName);

/* If a previous call to the function ended in error there may be some	*/
/* old keyword table pushed on the stack.  The keywords table stack is 	*/
/* initialized to zero to process the current configuration file	*/

  num_on_stack = 0;

/* Allocate required buffers                                            */

  cfg_line_buf = (ST_CHAR *) chk_calloc (cfg_line_buf_size, 
						sizeof(ST_CHAR));
  cfg_string_buf = (ST_CHAR *) chk_calloc (cfg_max_string_len+1, 
						sizeof(ST_CHAR));
  cfg_kw_stack = (struct cfg_kw_el **) chk_calloc (MAX_CONFIG_NEST, 
						sizeof (struct cfg_kw_el *));

/* Start with the ROOT keyword table                                    */

  cfg_tbl_push (root_kw_tbl);

/* Process the configuration file                                       */

  cfg_need_new_line = SD_TRUE;	
  cfg_line_offset = 0;		
  cfg_curr_line = 0;
  config_err = SD_FALSE;
  config_stop = SD_FALSE;
  config_eof = SD_FAILURE;

/* Process keywords while not done and no error is detected		*/

  while ( ( config_stop == SD_FALSE ) && ( config_err == SD_FALSE ) )
    {
    if ( cfg_get_next_keyword( config_fptr ) )
      break;
    }

/* All done, see if it went OK                                         */

  if ( ( config_stop == SD_TRUE ) && ( config_err == SD_FALSE ) )
    {
    CFG_LOG_CFLOW0 ("Config File Read OK");
    ret = SD_SUCCESS;
    }
  else
    {
    CFG_LOG_ERR3 ("Config Error, Line %d, \"%s\", Column %d",
          	cfg_curr_line, cfg_line_buf, cfg_line_offset);

    ret = SD_FAILURE;
    }

/* Free allocated buffers                                               */
  chk_free (cfg_line_buf);
  chk_free (cfg_string_buf);
  chk_free (cfg_kw_stack);

/* Close the file so the calling program can reopen it if necessary.	*/

  fclose( config_fptr );

  return (ret);
  }

/************************************************************************/
/************************************************************************/
/*                          cfg_get_next_keyword                        */
/************************************************************************/
/* This function is used to get the next keyword			*/

static ST_RET cfg_get_next_keyword (FILE *fptr)
  {
struct cfg_kw_el *ptr;
ST_INT i;
ST_CHAR c;

/* Look for 'yyy =' string                                              */

  while (SD_TRUE)			/* While looking for the next word      */
    {
    if (cfg_need_new_line)
      {
      if ( fgets( cfg_line_buf, cfg_line_buf_size - 1, fptr ) == NULL )
        {
        CFG_LOG_CFLOW0 ("No More Keywords : End Of File");

/* The following piece of logic exists to allow the state functions to	*/
/* declare when it is ok for the file to be out of keywords.  If a user */
/* state function set 'config_eof == SD_SUCCESS' then config_stop is set	*/
/* to true when ENDFILE is encountered.					*/

	if ( config_eof == SD_SUCCESS )
	  {
	  config_stop = SD_TRUE;
	  }
	return( SD_FAILURE );
        }

      /* Wack the \n                            */
      for (i = 0; i < cfg_line_buf_size; ++i)
        {
        if (cfg_line_buf[i] == '\n')
          {
          cfg_line_buf[i] = 0;
          break;
          }
        }

      cfg_line_offset = 0;		/* Offset is 0 for new line     */
      ++cfg_curr_line;
      if (cfg_line_buf[0] == '#')
	continue;			/* Discard comment lines        */

      cfg_need_new_line = SD_FALSE;	/* Got a new line               */
      }

/* We now have a line to work on, with cfg_line_offset the 1st char     */
/* Extract the keyword from the string (alpha numeric characters)       */
/*	'=' means end of rval                                          */
/*	0 is end of line                                                */

    i = 0;
    c = cfg_line_buf[cfg_line_offset++];
    while (c && c != '#' && 	/* End of line or comment start         */
           c != '=' &&       	/* end of keyword                       */
           c != '|' && 
	   i < cfg_max_string_len)
      {
      if (c != ' ' && c != '\t') /* Ignore spaces and tabs              */
        {
        cfg_string_buf[i] = c;
        ++i;
        }
      c = cfg_line_buf[cfg_line_offset++];
      }

    if (i >= cfg_max_string_len)
      {
      CFG_LOG_ERR1 ("Error : Line %d too long", cfg_curr_line);
      return (SD_FAILURE);
      }

    if (c == '#' || !c)	         /* See if we need a new line next time */
      cfg_need_new_line = SD_TRUE;

/* i is the index to the char position that terminated the scan         */
    cfg_string_buf[i]=0x0;		/* NULL terminate the keyword   */

/* if we have a keyword, then search look it up in the currently active	*/
/* keyword table and execute the selected function			*/

    if (strlen (cfg_string_buf))
      {				/* Find the keyword                     */
      if ((ptr = cfg_lookup_keyword (curr_table, cfg_string_buf)) != NULL)
	{
	CFG_LOG_CFLOW1 ("Keyword : %s",cfg_string_buf);
	(*ptr->extract)();
	break;
	}
      else		/* invalid keyword                              */
	{
	CFG_LOG_ERR1 ("Unknown Keyword : %s",cfg_string_buf);
	return (SD_FAILURE);
	}
      }
    } /* While SD_TRUE                                                     */

  return(SD_SUCCESS);
  }

/************************************************************************/
/*                       cfg_get_rval()				*/
/************************************************************************/

static ST_CHAR *cfg_get_rval(ST_VOID)
  {
ST_INT i;
ST_CHAR c;

  cfg_string_buf[0]=0x0;

/* Now get the rval (value).                                           */
/* search for 0 to note end of line	*/

  i = 0;
  c =  cfg_line_buf[cfg_line_offset++];

/* First strip leading white space (' ', '\t')                          */
  while (c == ' ' || c == '\t')
    c =  cfg_line_buf[cfg_line_offset++];

  while (c && 
         c != '#' && 
         !cfg_end_of_rval (c) && 
         i < cfg_max_rval_len-1)
    {
    if (isalnum (c) || cfg_special_char (c))
      {
      cfg_string_buf[i] = c;
      ++i;
      }
    c =  cfg_line_buf[cfg_line_offset++];
    }

  if (c == '#' || !c)	/* See if we need a new line next time          */
    cfg_need_new_line = SD_TRUE;

/* Strip trailing spaces and tabs                                       */
  while (i && (cfg_string_buf[i-1] == ' ' || 
               cfg_string_buf[i-1] == '\t'))
    i--;

/* Null terminate the string                                            */
  cfg_string_buf[i] = 0x0;

  if (!strlen (cfg_string_buf))
    {
    CFG_LOG_ERR2 ("Bad rval, line #%d \"%s\"",cfg_curr_line,cfg_line_buf);
    return (NULL);
    }

  CFG_LOG_CFLOW1 ("Rval    : '%s'",cfg_string_buf);
  return (cfg_string_buf);
  }

/************************************************************************/
/*			cfg_lookup_keyword 				*/
/************************************************************************/

static struct cfg_kw_el *cfg_lookup_keyword(struct cfg_kw_el *tbl_ptr,
	ST_CHAR *name)
  {
ST_INT i;
struct cfg_kw_el *ret = NULL;

  for (i = 0; i < MAX_NUM_KEYWORDS; ++i)
    {
    if (tbl_ptr[i].extract == NULL)		/* End of table         */
      break;
    else if (!strcmpi (name, tbl_ptr[i].name))
      {			      			/*  we have a match	*/
      if (tbl_ptr[i].valid !=NULL)		/* Want to validate?    */
        {
	if ((*tbl_ptr[i].valid)())
      	  ret = &tbl_ptr[i];
        }
      else
	ret = &tbl_ptr[i];

      break;
      }
    }

  return(ret);
  }

/************************************************************************/
/*                       cfg_end_of_rval  				*/
/************************************************************************/
/* CONFIG calls back to this function while looking the rval over      */
/* If this function returns SD_TRUE, CONFIG will assume that the           */
/* character is a keyword/rval pair seperator                          */
/* Used to allow multiple pairs per line                                */


static ST_RET cfg_end_of_rval (ST_CHAR c)
  {
ST_INT i;
ST_INT str_len;

  str_len = strlen (cfg_end_of_rval_str);
  for (i = 0; i < str_len; ++i)
    {
    if (c == cfg_end_of_rval_str[i])
      return (SD_TRUE);
    }
  return (SD_FALSE);
  }

/************************************************************************/
/*                       cfg_special_char					*/
/************************************************************************/

static ST_RET cfg_special_char (ST_CHAR c)
  {
ST_INT i;
ST_INT str_len;

  str_len = strlen (cfg_special_char_str);
  for (i = 0; i < str_len; ++i)
    {
    if (c == cfg_special_char_str[i])
      return (SD_TRUE);
    }

  return (SD_FALSE);
  }


/************************************************************************/
/************************************************************************/
/************************************************************************/
/* 			cfg_tbl_push (new_table)			*/
/************************************************************************/

ST_RET cfg_tbl_push (struct cfg_kw_el *new_table)
  {
  if (num_on_stack >= MAX_CONFIG_NEST)
    {
    CFG_LOG_ERR0 ("Too many pushes - configuration nesting exceeded");
    config_stop = SD_TRUE;
    config_err = CFG_UTIL_ERR;
    return (SD_FAILURE);
    }

  cfg_kw_stack[num_on_stack] = new_table;
  ++num_on_stack;
  curr_table = new_table;
  return (SD_SUCCESS);
  }

/************************************************************************/
/* 			cfg_tbl_pop (num_to_pop)			*/
/************************************************************************/

ST_RET cfg_tbl_pop (ST_INT num_to_pop)
  {
  if ((num_on_stack-num_to_pop-1)<0)
    {
    CFG_LOG_ERR0 ("Too many pops off configuration stack");
    config_stop = SD_TRUE;
    config_err = CFG_UTIL_ERR;
    return (SD_FAILURE);
    }
  num_on_stack -= num_to_pop;
  curr_table = cfg_kw_stack[num_on_stack-1];
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       cfg_get_short					*/
/************************************************************************/

ST_RET cfg_get_short (ST_INT16 *out_ptr)
  {
ST_INT d;
ST_INT16 ret;

  ret = cfg_get_value ("%d", &d);
  if (!ret)
    *out_ptr = (ST_INT16) d;  
  return (ret);
  }

/************************************************************************/
/*                       cfg_get_ushort					*/
/************************************************************************/

ST_RET cfg_get_ushort (ST_UINT16 *out_ptr)
  {
ST_UINT d;
ST_RET ret;

  ret = cfg_get_value ("%u", &d);
  if (!ret)
    *out_ptr = (ST_UINT16) d;  
  return (ret);
  }

/************************************************************************/
/*                       cfg_get_int					*/
/************************************************************************/

ST_RET cfg_get_int (ST_INT *out_ptr)
  {
ST_INT d;
ST_RET ret;

  ret = cfg_get_value ("%d", &d);
  if (!ret)
    *out_ptr = d;  
  return (ret);
  }

/************************************************************************/
/*                       cfg_get_uint					*/
/************************************************************************/

ST_RET cfg_get_uint (ST_UINT *out_ptr)
  {
ST_UINT d;
ST_RET ret;

  ret = cfg_get_value ("%u", &d);
  if (!ret)
    *out_ptr = d;  
  return (ret);
  }

/************************************************************************/
/*                       cfg_get_long					*/
/************************************************************************/

ST_RET cfg_get_long (ST_LONG *out_ptr)
  {
  return (cfg_get_value ("%ld", out_ptr));
  }

/************************************************************************/
/*                       cfg_get_ulong					*/
/************************************************************************/

ST_RET cfg_get_ulong (ST_ULONG *out_ptr)
  {
  return (cfg_get_value ("%lu", out_ptr));
  }


/************************************************************************/
/*                       cfg_get_double					*/
/************************************************************************/

ST_RET cfg_get_double (ST_DOUBLE *out_ptr)
  {
  return (cfg_get_value ("%lf", out_ptr));
  }


/************************************************************************/
/*                       cfg_get_hex_ushort 				*/
/************************************************************************/

ST_RET cfg_get_hex_ushort (ST_UINT16 *out_ptr)
  {
ST_UINT d;
ST_RET ret;

  ret = cfg_get_value ("%x", &d);
  if (!ret)
    *out_ptr = (ST_UINT16) d;  
  return (ret);

/*  return (cfg_get_value ("%x", out_ptr)); */
  }

/************************************************************************/
/*                       cfg_get_hex_uint 				*/
/************************************************************************/

ST_RET cfg_get_hex_uint (ST_UINT *out_ptr)
  {
ST_UINT d;
ST_RET ret;

  ret = cfg_get_value ("%x", &d);
  if (!ret)
    *out_ptr = d;  
  return (ret);

/*  return (cfg_get_value ("%x", out_ptr)); */
  }

/************************************************************************/
/*                       cfg_get_hex_ulong				*/
/************************************************************************/

ST_RET cfg_get_hex_ulong (ST_ULONG *out_ptr)
  {
  return (cfg_get_value ("%lx", out_ptr));
  }

/************************************************************************/
/*                       cfg_get_value					*/
/************************************************************************/

ST_RET cfg_get_value (ST_CHAR *format_string, ST_VOID *out_ptr)
  {
ST_CHAR *rval;

  rval = cfg_get_rval ();		/* get the rvalue string        */
  if (!rval)
    {
    config_err = CFG_GET_RVAL_ERR;
    CFG_LOG_ERR0 ("Get Rval Error");
    }
  else
    {                                   /* Convert to desired data format*/
    if (!sscanf (rval,format_string,out_ptr))
      {
      config_err = CFG_CONVERT_ERR;
      CFG_LOG_ERR0 ("Data Conversion Error");
      }
    }
  
  if (config_err)			/* if error detected ...        */
    {
    config_stop = SD_TRUE;
    return (SD_FAILURE);
    }

  return (SD_SUCCESS);
  }


/************************************************************************/
/************************************************************************/
/*                       *cfg_get_alloc_string				*/
/************************************************************************/

ST_CHAR *cfg_get_alloc_string (ST_VOID)
  {
ST_CHAR *ret_ptr;
ST_CHAR *rval;

  rval = cfg_get_rval ();		/* get the rvalue string        */
  if (!rval)
    {
    config_stop = SD_TRUE;
    config_err = CFG_GET_RVAL_ERR;
    CFG_LOG_ERR0 ("Get Rval Error");
    return (NULL);
    }

					/* allocate storage for string  */
  ret_ptr = (ST_CHAR *) chk_calloc (1,strlen (rval) +1);
  strcpy (ret_ptr,rval); 
  return (ret_ptr);
  }


/************************************************************************/
/*                       *cfg_get_string_ptr   				*/
/************************************************************************/

ST_CHAR *cfg_get_string_ptr (ST_VOID)
  {
ST_CHAR *rval;

  rval = cfg_get_rval ();		/* get the rvalue string        */
  if (!rval)
    {
    config_stop = SD_TRUE;
    config_err = CFG_GET_RVAL_ERR;
    CFG_LOG_ERR0 ("Get Rval Error");
    return (NULL);
    }

  return (rval);
  }

/************************************************************************/
/*                       cfg_get_octet_string   			*/
/************************************************************************/

ST_RET cfg_get_octet_string (ST_UCHAR *ostr,	/* ptr to user's ostr	*/
                ST_UINT *len_out_ptr,	/* addr of len var to be set	*/
                ST_UINT len_max)	/* maximum len to allow.	*/
  {
ST_UINT j;
ST_INT digit;
ST_BOOLEAN nibble;	/* SD_TRUE if nibble read, SD_FALSE if whole byte read*/
ST_CHAR *rval;

  rval = cfg_get_rval ();		/* get the rvalue string        */
  if (!rval)
    {
    config_stop = SD_TRUE;
    config_err = CFG_GET_RVAL_ERR;
    CFG_LOG_ERR0 ("Get Rval Error");
    return (SD_FAILURE);
    }

  j = 0;
  nibble = SD_FALSE;
  while (isxdigit (*rval))	/* get hex number			*/
    {
    if (j >= len_max)
      return (SD_FAILURE);	/* Selector longer than allowed	*/

    digit = *rval++;	/* separate nibbles			*/
    digit = isdigit (digit) ? digit - '0' : 10 + (toupper (digit) - 'A'); 
    if (nibble)
      {
      nibble = SD_FALSE;
      ostr[j] = (ostr[j] | (ST_UCHAR) digit);	/* set low */
      j++;

      while (isspace (*rval) && *rval != '\n')
        ++rval;
      }
    else
      {
      nibble = SD_TRUE;
      ostr[j] = (ST_UCHAR) digit << 4;		/* set high byte */
      }
    }

  if (nibble)
    {					/* Only got half of byte.	*/
    config_stop = SD_TRUE;
    config_err = CFG_CONVERT_ERR;
    CFG_LOG_ERR0 ("Incomplete byte in Octet string");
    return (SD_FAILURE);
    }

  /* set selector length						*/
  *len_out_ptr = j;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                          cfg_set_config_err                          */
/************************************************************************/

ST_VOID    cfg_set_config_err(ST_VOID)
  {
  config_err = SD_TRUE;
  }

/************************************************************************/
/*                          cfg_set_endfile_ok                          */
/************************************************************************/

ST_VOID    cfg_set_endfile_ok(ST_VOID)
  {
  config_eof = SD_SUCCESS;
  }



/************************************************************************/
/************************************************************************/
/*                          cfg_goto_keyword                            */
/************************************************************************/
/* This function is used to skip over a section to the specified keyword*/

ST_RET cfg_goto_keyword (ST_CHAR *keyword)
  {
ST_INT i, cfgStringLen, keywordLen;
ST_CHAR c;

/* Look for 'yyy =' string                                              */

  keywordLen = strlen (keyword);
  while (SD_TRUE)	   /* While looking for the specified word      */
    {
    if (cfg_need_new_line)
      {
      if ( fgets( cfg_line_buf, cfg_line_buf_size - 1, config_fptr ) == NULL )
        {
        CFG_LOG_CFLOW0 ("No More Keywords : End Of File");

/* The following piece of logic exists to allow the state functions to	*/
/* declare when it is ok for the file to be out of keywords.  If a user */
/* state function set 'config_eof == SD_SUCCESS' then config_stop is set	*/
/* to true when ENDFILE is encountered.					*/

	if ( config_eof == SD_SUCCESS )
	  {
	  config_stop = SD_TRUE;
	  }
	return( SD_FAILURE );
        }

      /* Wack the \n                            */
      for (i = 0; i < cfg_line_buf_size; ++i)
        {
        if (cfg_line_buf[i] == '\n')
          {
          cfg_line_buf[i] = 0;
          break;
          }
        }

      cfg_line_offset = 0;		/* Offset is 0 for new line     */
      ++cfg_curr_line;
      if (cfg_line_buf[0] == '#')
	continue;			/* Discard comment lines        */

      cfg_need_new_line = SD_FALSE;	/* Got a new line               */
      }

/* We now have a line to work on, with cfg_line_offset the 1st char     */
/* Extract the keyword from the string (alpha numeric characters)       */
/*	'=' means end of rval                                          */
/*	0 is end of line                                                */

    i = 0;
    c = cfg_line_buf[cfg_line_offset++];
    while (c && c != '#' && 	/* End of line or comment start         */
           c != '=' &&       	/* end of keyword                       */
           c != '|' && 
	   i < cfg_max_string_len)
      {
      if (c != ' ' && c != '\t') /* Ignore spaces and tabs              */
        {
        cfg_string_buf[i] = c;
        ++i;
        }
      c = cfg_line_buf[cfg_line_offset++];
      }

    if (i >= cfg_max_string_len)
      {
      CFG_LOG_ERR1 ("Error : Line %d too long", cfg_curr_line);
      return (SD_FAILURE);
      }

    if (c == '#' || !c)	         /* See if we need a new line next time */
      cfg_need_new_line = SD_TRUE;

/* i is the index to the char position that terminated the scan         */
    cfg_string_buf[i]=0x0;		/* NULL terminate the keyword   */

/* if we have a keyword, then search look it up in the currently active	*/
/* keyword table and execute the selected function			*/

    if ((cfgStringLen = strlen (cfg_string_buf)) > 0)
      {				/* Find the keyword                     */
      if ((cfgStringLen == keywordLen) && !strcmpi (keyword, cfg_string_buf))
        break;
      else		/* keyword not found */
        cfg_need_new_line = SD_TRUE;
      }
    } /* While SD_TRUE                                                     */

  return(SD_SUCCESS);
  }
















