///
/// @file config.c
/// @authors k. Headley
/// @date 05 dec 2019

/// Configuration API

/// @sa doxygen-examples.c for more examples of Doxygen markup


/////////////////////////
// Terms of use
/////////////////////////
/*
 Copyright Information

 Copyright 2002-2019 MBARI
 Monterey Bay Aquarium Research Institute, all rights reserved.

 Terms of Use

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version. You can access the GPLv3 license at
 http://www.gnu.org/licenses/gpl-3.0.html

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details
 (http://www.gnu.org/licenses/gpl-3.0.html)

 MBARI provides the documentation and software code "as is", with no warranty,
 express or implied, as to the software, title, non-infringement of third party
 rights, merchantability, or fitness for any particular purpose, the accuracy of
 the code, or the performance or results which you may obtain from its use. You
 assume the entire risk associated with use of the code, and you agree to be
 responsible for the entire cost of repair or servicing of the program with
 which you are using the code.

 In no event shall MBARI be liable for any damages, whether general, special,
 incidental or consequential damages, arising out of your use of the software,
 including, but not limited to, the loss or corruption of your data or damages
 of any kind resulting from use of the software, any prohibited use, or your
 inability to use the software. You agree to defend, indemnify and hold harmless
 MBARI and its officers, directors, and employees against any claim, loss,
 liability or expense, including attorneys' fees, resulting from loss of or
 damage to property or the injury to or death of any person arising out of the
 use of the software.

 The MBARI software is provided without obligation on the part of the
 Monterey Bay Aquarium Research Institute to assist in its use, correction,
 modification, or enhancement.

 MBARI assumes no responsibility or liability for any third party and/or
 commercial software required for the database or applications. Licensee agrees
 to obtain and maintain valid licenses for any additional third party software
 required.
 */

/////////////////////////
// Headers
/////////////////////////
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "config.h"
#include "ioserial.h"
#include "ui.h"
#include "iowfile.h"

/////////////////////////
// Macros
/////////////////////////

// These macros should only be defined for
// application main files rather than general C files
/*
/// @def PRODUCT
/// @brief header software product name
#define PRODUCT "carbonwg"

/// @def COPYRIGHT
/// @brief header software copyright info
#define COPYRIGHT "Copyright 2002-2019 MBARI Monterey Bay Aquarium Research Institute, all rights reserved."
/// @def NOWARRANTY
/// @brief header software terms of use
#define NOWARRANTY  \
"This program is distributed in the hope that it will be useful,\n"\
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"\
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"\
"GNU General Public License for more details (http://www.gnu.org/licenses/gpl-3.0.html)\n"
*/

#define CAST_VAL(pv,type)  ((type)(*((type *)pv)))
#define CAST_PTR(pv,type)  ((type *)pv)

/////////////////////////
// Declarations
/////////////////////////
/// @typedef enum cfg_bool_idx cfg_bool_idx
/// @brief bool config value indices
typedef enum{
    CID_VERBOSE=0,
    CID_LOG_RAW,
    CID_AUTO_OUT,
    CID_BOOL_COUNT
}cfg_bool_idx;

/// @typedef enum cfg_u16_idx cfg_u16_idx
/// @brief u16 config value indices
typedef enum{
    CID_SERIAL_N=0,
    CID_VALVE_TYPE,
    CID_ECHO,
    CID_PROMPT,
    CID_U16_COUNT
}cfg_u16_idx;

/// @typedef enum cfg_i16_idx cfg_i16_idx
/// @brief i16 config value indices
typedef enum{
    CID_LICOR_MODEL=0,
    CID_I16_COUNT
}cfg_i16_idx;

/// @typedef enum cfg_u32_idx cfg_u32_idx
/// @brief u32 config value indices
typedef enum{
    CID_FOO_U32=0,
    CID_LICOR_CFLAGS,
    CID_LICOR_OFLAGS,
    CID_U32_COUNT
}cfg_u32_idx;

/// @typedef enum cfg_i32_idx cfg_i32_idx
/// @brief i32 config value indices
typedef enum{
    CID_FOO_I32=0,
    CID_I32_COUNT
}cfg_i32_idx;
/// @typedef enum cfg_double_idx cfg_double_idx
/// @brief double config value indices
typedef enum{
    CID_FOO_DBL=0,
    CID_LICOR_SPANC,
    CID_DOUBLE_COUNT
}cfg_double_idx;

/// @typedef enum cfg_byte_idx cfg_byte_idx
/// @brief byte config value indices
typedef enum{
    CID_FOO_BYTE=0,
    CID_BYTE_COUNT
}cfg_byte_idx;

/// @typedef enum cfg_string_idx cfg_string_idx
/// @brief string config value indices
typedef enum{
    CID_FOO_S=0,
    CID_CBIN_S,
    CID_CBUF_S,
    CID_OBIN_S,
    CID_ZBIN_S,
    CID_SYS_S,
    CID_S0_S,
    CID_S1_S,
    CID_S2_S,
    CID_S3_S,
    CID_STRING_COUNT
}cfg_string_idx;

void s_assign_byte(void *dest, void *src);
void s_assign_u16(void *dest, void *src);
void s_assign_i16(void *dest, void *src);
void s_assign_u32(void *dest, void *src);
void s_assign_i32(void *dest, void *src);
void s_assign_u64(void *dest, void *src);
void s_assign_i64(void *dest, void *src);
void s_assign_double(void *dest, void *src);
void s_assign_bool(void *dest, void *src);
void s_assign_string(void *dest, void *src);

int s_parse_byte(void *dest, char *src);
int s_parse_u16(void *dest, char *src);
int s_parse_i16(void *dest, char *src);
int s_parse_u32(void *dest, char *src);
int s_parse_i32(void *dest, char *src);
int s_parse_u64(void *dest, char *src);
int s_parse_i64(void *dest, char *src);
int s_parse_double(void *dest, char *src);
int s_parse_bool(void *dest, char *src);
int s_parse_string(void *dest, char *src);

char *s_str_byte(char *dest, const char *fmt, void *pval);
char *s_str_u16(char *dest, const char *fmt, void *pval);
char *s_str_i16(char *dest, const char *fmt, void *pval);
char *s_str_u32(char *dest, const char *fmt, void *pval);
char *s_str_i32(char *dest, const char *fmt, void *pval);
char *s_str_u64(char *dest, const char *fmt, void *pval);
char *s_str_i64(char *dest, const char *fmt, void *pval);
char *s_str_double(char *dest, const char *fmt, void *pval);
char *s_str_bool(char *dest, const char *fmt, void *pval);
char *s_str_string(char *dest, const char *fmt, void *pval);

bool s_boolval_byte(void *pval);
bool s_boolval_u16(void *pval);
bool s_boolval_i16(void *pval);
bool s_boolval_u32(void *pval);
bool s_boolval_i32(void *pval);
bool s_boolval_u64(void *pval);
bool s_boolval_i64(void *pval);
bool s_boolval_double(void *pval);
bool s_boolval_bool(void *pval);
bool s_boolval_string(void *pval);

double s_doubleval_byte(void *pval);
double s_doubleval_u16(void *pval);
double s_doubleval_i16(void *pval);
double s_doubleval_u32(void *pval);
double s_doubleval_i32(void *pval);
double s_doubleval_u64(void *pval);
double s_doubleval_i64(void *pval);
double s_doubleval_double(void *pval);
double s_doubleval_bool(void *pval);
double s_doubleval_string(void *pval);

return_code_t config_parse_cx(const char *key, char *value);
return_code_t config_parse_dx(const char *key, char *value);
return_code_t config_parse_fx(const char *key, char *value);
return_code_t config_get_cx(const char *key, uint32_t *val);
return_code_t config_get_dx(const char *key, double *val);
return_code_t config_get_fx(const char *key, bool *val);

/////////////////////////
// Imports
/////////////////////////

/////////////////////////
// Module Global Variables
/////////////////////////

/// @var cfg_type_strings
/// @brief variable type name strings
/// configuration types and IO format strings
/// indexed by type ids
/// [ so must reflect changes to config_type_id]
///    CTI_BYTE,
///    CTI_U16,
///    CTI_I16,
///    CTI_U32,
///    CTI_I32,
///    CTI_U64,
///    CTI_I64,
///    CTI_DOUBLE,
///    CTI_BOOL,
///    CTI_STRING
const char *cfg_type_strings[]={
    "byte",
    "uint16",
    "int16",
    "uint32",
    "int32",
    "uint64",
    "int64",
    "double",
    "bool",
    "string"
};

/// @var cfg_fmt_strings
/// @brief configuration default parse/output format specifiers
///    CTI_BYTE,
///    CTI_U16,
///    CTI_I16,
///    CTI_U32,
///    CTI_I32,
///    CTI_U64,
///    CTI_I64,
///    CTI_DOUBLE,
///    CTI_BOOL,
///    CTI_STRING
const char *cfg_fmt_strings[]={
    "X%02X",
    "X%04X",
    "%d",
    "X%08lX",
    "%ld",
    "X%016llX",
    "%lld",
    "%lg",
    "%c",
    "%s"
};

/// @var char g_cfg_strval[CFG_MEM_STRING_BYTES]
/// @brief buffer for strval return (non-threadsafe, non-reentrant)
char g_cfg_strval[CFG_MEM_STRING_BYTES];

/// @var bool cfg_mem_bool[CID_BOOL_COUNT]
/// @brief config bool value memory.
/// Indexed using cfg_bool_idx
bool cfg_mem_bool[CID_BOOL_COUNT]={
    CFG_VERBOSE_DFL,
    CFG_LOG_RAW_DFL,
    CFG_AUTO_OUT_DFL
};

/// @var uint16_t cfg_mem_u16[CID_U16_COUNT]
/// @brief config u16 value memory.
/// Indexed using cfg_u16_idx
uint16_t cfg_mem_u16[CID_U16_COUNT]={
    CFG_SERIAL_UNASSIGNED,
    VTYPE_MONO,
    CFG_ECHO_DFL,
    CFG_PROMPT_DFL
};
/// @var int16_t cfg_mem_i16[CID_I16_COUNT]
/// @brief config i16 value memory.
/// Indexed using cfg_i16_idx
uint16_t cfg_mem_i16[CID_I16_COUNT]={
    LI_MODEL_850
};
/// @var uint32_t cfg_mem_u32[CID_U32_COUNT]
/// @brief config u32 value memory.
/// Indexed using cfg_u32_idx
uint32_t cfg_mem_u32[CID_U32_COUNT]={
    CFG_FOO_U32_DFL,
    CFG_LICOR_CFLAGS_DFL,
    CFG_LICOR_OFLAGS_DFL
};
/// @var int32_t cfg_mem_i32[CID_I32_COUNT]
/// @brief config i32 value memory.
/// Indexed using cfg_i32_idx
uint32_t cfg_mem_i32[CID_I32_COUNT]={
    CFG_FOO_I32_DFL
};
/// @var double cfg_mem_double[CID_DOUBLE_COUNT]
/// @brief config double value memory.
/// Indexed using cfg_double_idx
double cfg_mem_double[CID_DOUBLE_COUNT]={
    CFG_FOO_DBL_DFL,
    CFG_LICOR_SPANC_DFL
};

/// @var byte cfg_mem_byte[CID_BYTE_COUNT]
/// @brief config byte value memory.
/// Indexed using cfg_byte_idx
byte cfg_mem_byte[CID_BYTE_COUNT]={
    CFG_FOO_BYTE_DFL
};

/// @var char cfg_mem_string[CID_STRING_COUNT][CFG_MEM_STRING_BYTES]
/// @brief config string value memory.
/// Indexed using cfg string idx
char cfg_mem_string[CID_STRING_COUNT][CFG_MEM_STRING_BYTES]={
    CFG_FOO_S_DFL,
    CFG_CBIN_S_DFL,
    CFG_CBUF_S_DFL,
    CFG_OBIN_S_DFL,
    CFG_ZBIN_S_DFL,
    CFG_SYS_S_DFL,
    CFG_S0_S_DFL,
    CFG_S1_S_DFL,
    CFG_S2_S_DFL
};

/// @var config_funcs_t cfg_fns_byte
/// @brief byte variable parsing/conversion functions
config_funcs_t cfg_fns_byte  ={s_assign_byte,   s_parse_byte,   s_str_byte, s_boolval_byte, s_doubleval_byte};
/// @var config_funcs_t cfg_fns_u16
/// @brief uint16 variable parsing/conversion functions
config_funcs_t cfg_fns_u16   ={s_assign_u16,    s_parse_u16,    s_str_u16, s_boolval_u16, s_doubleval_u16};
/// @var config_funcs_t cfg_fns_i16
/// @brief int16 variable parsing/conversion functions
config_funcs_t cfg_fns_i16   ={s_assign_i16,    s_parse_i16,    s_str_i16, s_boolval_i16, s_doubleval_i16};
/// @var config_funcs_t cfg_fns_u32
/// @brief uint32 variable parsing/conversion functions
config_funcs_t cfg_fns_u32   ={s_assign_u32,    s_parse_u32,    s_str_u32, s_boolval_u32, s_doubleval_u32};
/// @var config_funcs_t cfg_fns_i32
/// @brief int32 variable parsing/conversion functions
config_funcs_t cfg_fns_i32   ={s_assign_i32,    s_parse_i32,    s_str_i32, s_boolval_i32, s_doubleval_i32};
/// @var config_funcs_t cfg_fns_u64
/// @brief uint64 variable parsing/conversion functions
config_funcs_t cfg_fns_u64   ={s_assign_u64,    s_parse_u64,    s_str_u64, s_boolval_u64, s_doubleval_u64};
/// @var config_funcs_t cfg_fns_i64
/// @brief int64 variable parsing/conversion functions
config_funcs_t cfg_fns_i64   ={s_assign_i64,    s_parse_i64,    s_str_i64, s_boolval_i64, s_doubleval_i64};
/// @var config_funcs_t cfg_fns_bool
/// @brief bool variable parsing/conversion functions
config_funcs_t cfg_fns_bool  ={s_assign_bool,   s_parse_bool  , s_str_bool, s_boolval_bool, s_doubleval_bool};
/// @var config_funcs_t cfg_fns_double
/// @brief double variable parsing/conversion functions
config_funcs_t cfg_fns_double={s_assign_double, s_parse_double, s_str_double, s_boolval_double, s_doubleval_double};
/// @var config_funcs_t cfg_fns_string
/// @brief string variable parsing/conversion functions
config_funcs_t cfg_fns_string={s_assign_string, s_parse_string, s_str_string, s_boolval_string, s_doubleval_string};

/// @var config_item_t config_table[]
/// @brief configuration item table.
/// Value pointers reference cfg_mem_X arrays.
/// can use function pointer members or store sets of function pointers (by type).
/// latter saves memory if >1 entry of a type, otherwise, adds a pointer)

config_item_t config_table[]={
    {CK_VERBOSE,     CTI_BOOL, (void *)&cfg_mem_bool[CID_VERBOSE],   &cfg_fns_bool,NULL},
    {CK_LOG_RAW,     CTI_BOOL, (void *)&cfg_mem_bool[CID_LOG_RAW],   &cfg_fns_bool,NULL},
    {CK_AUTO_OUT,    CTI_BOOL, (void *)&cfg_mem_bool[CID_AUTO_OUT],  &cfg_fns_bool,NULL},
    {CK_LICOR_MODEL, CTI_I16,  (void *)&cfg_mem_i16[CID_LICOR_MODEL],&cfg_fns_i16,NULL},
    {CK_LICOR_SPANC, CTI_DOUBLE,(void *)&cfg_mem_double[CID_LICOR_SPANC],&cfg_fns_double,NULL},
    {CK_LICOR_CFLAGS,CTI_U32,   (void *)&cfg_mem_u32[CID_LICOR_CFLAGS],   &cfg_fns_u32,licor_mnem2cflag},
    {CK_LICOR_OFLAGS,CTI_U32,   (void *)&cfg_mem_u32[CID_LICOR_OFLAGS],   &cfg_fns_u32,licor_mnem2oflag},
    {CK_SERIAL_N,    CTI_U16,  (void *)&cfg_mem_u16[CID_SERIAL_N],   &cfg_fns_u16,NULL},
    {CK_VALVE_TYPE,  CTI_U16,  (void *)&cfg_mem_u16[CID_VALVE_TYPE], &cfg_fns_u16,NULL},
    {CK_ECHO,        CTI_U16,  (void *)&cfg_mem_u16[CID_ECHO],       &cfg_fns_u16,NULL},
    {CK_PROMPT,      CTI_U16,  (void *)&cfg_mem_u16[CID_PROMPT],     &cfg_fns_u16,NULL},
    {CK_CBIN_S,      CTI_STRING,(void *)&cfg_mem_string[CID_CBIN_S], &cfg_fns_string,NULL},
    {CK_CBUF_S,      CTI_STRING,(void *)&cfg_mem_string[CID_CBUF_S], &cfg_fns_string,NULL},
    {CK_OBIN_S,      CTI_STRING,(void *)&cfg_mem_string[CID_OBIN_S], &cfg_fns_string,NULL},
    {CK_ZBIN_S,      CTI_STRING,(void *)&cfg_mem_string[CID_ZBIN_S], &cfg_fns_string,NULL},
    {CK_SYS_S,       CTI_STRING,(void *)&cfg_mem_string[CID_SYS_S],  &cfg_fns_string,NULL},
    {CK_S0_S,        CTI_STRING,(void *)&cfg_mem_string[CID_S0_S],   &cfg_fns_string,NULL},
    {CK_S1_S,        CTI_STRING,(void *)&cfg_mem_string[CID_S1_S],   &cfg_fns_string,NULL},
    {CK_S2_S,        CTI_STRING,(void *)&cfg_mem_string[CID_S2_S],   &cfg_fns_string,NULL},
    {CK_S3_S,        CTI_STRING,(void *)&cfg_mem_string[CID_S3_S],   &cfg_fns_string,NULL},
    {CK_FOO_BYTE,    CTI_BYTE, (void *)&cfg_mem_byte[CID_FOO_BYTE],  &cfg_fns_byte,NULL},
    {CK_FOO_S,       CTI_STRING,(void *)&cfg_mem_string[CID_FOO_S],  &cfg_fns_string,NULL},
    {CK_FOO_U32,     CTI_U32,   (void *)&cfg_mem_u32[CID_FOO_U32],   &cfg_fns_u32,NULL},
    {CK_FOO_I32,     CTI_I32,   (void *)&cfg_mem_i32[CID_FOO_I32],   &cfg_fns_i32,NULL},
    {CK_FOO_DBL,     CTI_DOUBLE,(void *)&cfg_mem_double[CID_FOO_DBL],&cfg_fns_double,NULL},
    {NULL,           CTI_BYTE,  (void *)NULL, NULL}
};

/// @var uint32_t cfg_cx_mem[CFG_CX_COUNT]
/// @brief CX register memory
uint32_t cfg_cx_mem[CFG_CX_COUNT]={0};
/// @var uint32_t cfg_fx_mem
/// @brief FX register memory
uint32_t cfg_fx_mem=0;
/// @var double cfg_dx_mem[CFG_DX_COUNT]
/// @brief DX register memory
double cfg_dx_mem[CFG_DX_COUNT]={0};

/////////////////////////
// Function Definitions
/////////////////////////

config_item_t *s_lookup_item(const char *key)
{
    config_item_t *retval = NULL;
    config_item_t *item=config_table;
    while(NULL!=item->key && NULL!=item->pval){
//            DPRINT("%s - key[%s/%d] item[%s/%d/%p]\r\n",__FUNCTION__,key,strlen(key),item->key,strlen(item->key),item);
        if(strcmp(item->key,key)==0){
//            DPRINT("%s - returning[%p]\r\n",__FUNCTION__,item);
            retval=item;
            break;
        }
        item++;
    }
    return retval;
}

void config_show(config_item_t *pself, bool verbose, int indent)
{
    config_item_t *self = (NULL==pself?config_table : pself);
    if( NULL!=self ){
        int wkey=12;
        int wval=16;

        if(verbose){
            OUT_ERR("%*s%*s %*p%s",indent,(indent>0?" ":""), wkey,"self",wval,self,LINEEND);
        }
         if(NULL!=self->pval){
            config_item_t *pi=self;
            char *vbuf = (char *)palloc(CFG_LINE_BYTES);

//            while(NULL!=pi->key){
//                OUT_ERR("%*s%*s %*s%s",indent+1,(indent>0?" ":""), wkey,"key",wval,pi->key,LINEEND);
//                if(verbose){
//                OUT_ERR("%*s%*s %*s%s",indent+1,(indent>0?" ":""), wkey,"type",wval,cfg_type_strings[pi->type],LINEEND);
//                OUT_ERR("%*s%*s %*p]%s",indent+1,(indent>0?" ":""), wkey,"pval",wval,pi->pval,LINEEND);
//                }
//                ZERO_MEM(vbuf,CFG_LINE_BYTES);
//                pi->funcs->string(vbuf,cfg_fmt_strings[pi->type],pi->pval);
//                OUT_ERR("%*s%*s %*s%s",indent+1,(indent>0?" ":""), wkey,"val",wval,vbuf,LINEEND);
//                pi++;
//            }
            while(NULL!=pi->key){
                OUT_CTTY("%*s%*s =",indent,(indent>0?" ":""), wkey,pi->key);
                ZERO_MEM(vbuf,CFG_LINE_BYTES);
                pi->funcs->string(vbuf,cfg_fmt_strings[pi->type],pi->pval);
                OUT_CTTY(" %-*s", wval,vbuf);
                if(verbose){
                OUT_CTTY(" [%s/%p]",cfg_type_strings[pi->type],pi->pval);
                }
                OUT_CTTY("%s",LINEEND);
                pi++;
            }
            pfree(vbuf);
        }
    }
   return;
}

char *s_str_byte(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,CAST_VAL(pval,byte));
        retval=op;
    }
    return retval;
}
char *s_str_u16(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,CAST_VAL(pval,uint16_t));
        retval=op;
    }
    return retval;
}
char *s_str_i16(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,CAST_VAL(pval,int16_t));
        retval=op;
    }
    return retval;
}
char *s_str_u32(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
     char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,CAST_VAL(pval,uint32_t));
        retval=op;
    }
    return retval;
}
char *s_str_i32(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,CAST_VAL(pval,int32_t));
        retval=op;
    }
    return retval;
}
char *s_str_u64(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,CAST_VAL(pval,uint64_t));
        retval=op;
    }
    return retval;
}
char *s_str_i64(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,CAST_VAL(pval,int64_t));
        retval=op;
    }
    return retval;
}
char *s_str_double(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt, CAST_VAL(pval,double));
        retval=op;
    }
    return retval;
}
char *s_str_bool(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
    char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,"%c", (CAST_VAL(pval,bool) ? '1':'0'));
        retval=op;
    }
    return retval;
}
char *s_str_string(char *dest, const char *fmt, void *pval)
{
    char *retval=NULL;
     char *op = (dest==NULL ? g_cfg_strval : dest);
    if(NULL!=fmt && NULL!=pval){
        sprintf(op,fmt,(char *)pval);
        retval=op;
    }
    return retval;
}

void s_assign_byte(void *dest, void *src)  {*((byte *)dest)    = *((byte *)src); }
void s_assign_u16(void *dest, void *src)   {*((uint16_t *)dest)= *((uint16_t *)src); }
void s_assign_i16(void *dest, void *src)   {*((int16_t *)dest) = *((int16_t *)src); }
void s_assign_u32(void *dest, void *src)   {*((uint32_t *)dest)= *((uint32_t *)src); }
void s_assign_i32(void *dest, void *src)   {*((int32_t *)dest) = *((int32_t *)src); }
void s_assign_u64(void *dest, void *src)   {*((uint64_t *)dest)= *((uint64_t *)src); }
void s_assign_i64(void *dest, void *src)   {*((int64_t *)dest) = *((int64_t *)src); }
void s_assign_double(void *dest, void *src){*((double *)dest)  = *((double *)src); }
void s_assign_bool(void *dest, void *src)  {*((bool *)dest)    = *((bool *)src); }

bool s_boolval_byte(void *pval){return (CAST_VAL(pval,byte)==0x00 ? false : true);}
bool s_boolval_u16(void *pval){return (CAST_VAL(pval,uint16_t)==0 ? false : true);}
bool s_boolval_i16(void *pval){return (CAST_VAL(pval,int16_t)==0 ? false : true);}
bool s_boolval_u32(void *pval){return (CAST_VAL(pval,uint32_t)==0L ? false : true);}
bool s_boolval_i32(void *pval){return (CAST_VAL(pval,int32_t)==0L ? false : true);}
bool s_boolval_u64(void *pval){return (CAST_VAL(pval,uint64_t)==0ul ? false : true);}
bool s_boolval_i64(void *pval){return (CAST_VAL(pval,int64_t)==0ll ? false : true);}
bool s_boolval_double(void *pval){return (CAST_VAL(pval,double)==0.0 ? false : true);}
bool s_boolval_bool(void *pval){return (CAST_VAL(pval,bool)==false ? false : true);}
bool s_boolval_string(void *pval){return ((char *)pval==NULL ? false : true);}

double s_doubleval_byte(void *pval){byte v=CAST_VAL(pval,byte); double d=v; return d;}
double s_doubleval_u16(void *pval){uint16_t v=CAST_VAL(pval,uint16_t); double d=v; return d;}
double s_doubleval_i16(void *pval){int16_t v=CAST_VAL(pval,int16_t); double d=v; return d;}
double s_doubleval_u32(void *pval){uint32_t v=CAST_VAL(pval,uint32_t); double d=v; return d;}
double s_doubleval_i32(void *pval){int32_t v=CAST_VAL(pval,int32_t); double d=v; return d;}
double s_doubleval_u64(void *pval){uint64_t v=CAST_VAL(pval,uint64_t); double d=v; return d;}
double s_doubleval_i64(void *pval){int64_t v=CAST_VAL(pval,int64_t); double d=v; return d;}
double s_doubleval_double(void *pval){double v=CAST_VAL(pval,double); double d=v; return d;}
double s_doubleval_bool(void *pval){bool v=CAST_VAL(pval,bool); double d=(v?1.0:0.0); return d;}
double s_doubleval_string(void *pval){return 0.0;}

// use only for cfg strings len<CFG_MEM_STRING_BYTES
void s_assign_string(void *dest, void *src)
{
    if(NULL!=dest && NULL!=src){
        char **pp=(char **)dest;
        char *dp = *pp;
        char *sp=(char *)src;
        size_t len = strlen(sp);
        len = (len > (CFG_MEM_STRING_BYTES-1) ? (CFG_MEM_STRING_BYTES-1) : len);
        MPRINT(MOD_CFG,"%s - pp[%p] dp[%p] src[%s] len[%zu]\r\n",__FUNCTION__,pp,dp,sp,len);
        if( (strncpy(dp,sp,len)!=NULL) ){
            dp[len]='\0';
        }
    }
}

int s_parse_byte(void *dest, char *src)
{
    int retval=-1;
    char c=0;
    uint16_t u16;
    if( *src=='x' || *src=='X' ){
        src++;
    }
    if(*src=='\'' && sscanf(src+1,"%c",&c)==1){
        *(CAST_PTR(dest,byte))=c;
        retval=0;
    }else if(sscanf(src,"%02X",&u16)==1 ||
        sscanf(src,"%02x",&u16)==1){
        *(CAST_PTR(dest,byte))=(byte)u16;
        retval=0;
    }else{
       sscanf(src,"%c",CAST_PTR(dest,char));
       retval=0;
    }
    return retval;
}

int s_parse_u16(void *dest, char *src)
{
   int retval=-1;
    if( *src=='x' || *src=='X' ){
       src++;
    }
    char c=0;
    if(*src=='\'' && sscanf(src+1,"%c",&c)==1){
         *(CAST_PTR(dest,uint16_t))=c;
        retval=0;
    }else if(sscanf(src,"%04X",CAST_PTR(dest,uint16_t))==1 ||
        sscanf(src,"%04x",CAST_PTR(dest,uint16_t))==1){
        retval=0;
    }else if(sscanf(src,"%d",CAST_PTR(dest,uint16_t))==1){
        retval=0;
    }

   return retval;
}

int s_parse_i16(void *dest, char *src)
{
   int retval=-1;
     char c=0;
    if(*src=='\'' && sscanf(src+1,"%c",&c)==1){
         *(CAST_PTR(dest,int16_t))=c;
        retval=0;
   }else if(sscanf(src,"%d",CAST_PTR(dest,int16_t))==1){
       retval=0;
   }
   return retval;
}

int s_parse_u32(void *dest, char *src)
{
   int retval=-1;
     if( *src=='x' || *src=='X' ){
        src++;
     }
    char c=0;
    if(*src=='\'' && sscanf(src+1,"%c",&c)==1){
         *(CAST_PTR(dest,uint32_t))=c;
        retval=0;
    }else if(sscanf(src,"%08lX",CAST_PTR(dest,uint32_t))==1 ||
        sscanf(src,"%08lx",CAST_PTR(dest,uint32_t))==1){
        retval=0;
    }else{
       if(sscanf(src,"%ld",CAST_PTR(dest,uint32_t))==1){
           retval=0;
       }
    }
   return retval;
}

int s_parse_i32(void *dest, char *src)
{
   int retval=-1;
    char c=0;
    if(*src=='\'' && sscanf(src+1,"%c",&c)==1){
         *(CAST_PTR(dest,int32_t))=c;
         retval=0;
   }else if(sscanf(src,"%ld",CAST_PTR(dest,int32_t))==1){
       retval=0;
   }
   return retval;
}

int s_parse_u64(void *dest, char *src)
{
   int retval=-1;
     if( *src=='x' || *src=='X' ){
        src++;
     }
    char c=0;
    if(*src=='\'' && sscanf(src+1,"%c",&c)==1){
         *(CAST_PTR(dest,uint64_t))=c;
        retval=0;
   }else if(sscanf(src,"%016llX",CAST_PTR(dest,uint64_t))==1 ||
        sscanf(src,"%016llx",CAST_PTR(dest,uint64_t))==1){
        retval=0;
    }else{
       if(sscanf(src,"%llu",CAST_PTR(dest,uint64_t))==1){
           retval=0;
       }
    }
   return retval;
}

int s_parse_i64(void *dest, char *src)
{
   int retval=-1;
   char c=0;
    if(*src=='\'' && sscanf(src+1,"%c",&c)==1){
         *(CAST_PTR(dest,int64_t))=c;
        retval=0;
   }else if(sscanf(src,"%lld",CAST_PTR(dest,int64_t))==1){
       retval=0;
   }
   return retval;
}

int s_parse_double(void *dest, char *src)
{
   int retval=-1;
  if(sscanf(src,"%lf",CAST_PTR(dest,double))==1){
       retval=0;
   }
   return retval;
}

int s_parse_bool(void *dest, char *src)
{
   int retval=0;
   switch(*src){
       case 'Y':
       case 'y':
       case 'T':
       case 't':
       case '1':
           *(CAST_PTR(dest,bool)) = true;
           break;
       case 'N':
       case 'n':
       case 'F':
       case 'f':
       case '0':
           *(CAST_PTR(dest,bool)) = false;
           break;

       default:
           retval=-1;
           break;
   }
   return retval;
}

// use only for cfg strings len<CFG_MEM_STRING_BYTES
int s_parse_string(void *dest, char *src )
{
    int retval=-1;
    if(NULL!=dest && NULL!=src){
        char **pp = (char **)dest;
        char *dp = *pp;
        size_t len = strlen(src);
        len = (len>(CFG_MEM_STRING_BYTES-1) ? (CFG_MEM_STRING_BYTES-1) : len );

        if(NULL!=dp && (strncpy(dp,src,len)!=NULL) ){
            dp[len]='\0';
            retval=0;
        }
    }
   return retval;
}

static void s_parse_item(config_item_t *dest, char *src)
{
    char *cp=NULL;
    if(NULL!=dest && NULL!=src){

        // skip whitespace, '='
        cp = src;
        while( (isspace (*cp) ||  *cp=='=' ) && cp<(src+strlen(src)) ) cp++;

        if(dest->type==CTI_STRING){
            dest->funcs->parse(&dest->pval,cp);
        }else{
            if(NULL!=dest->alt_parse){
                if(dest->alt_parse(dest->pval,cp)!=0){
                    dest->funcs->parse(dest->pval,cp);
                }
            }else{
                dest->funcs->parse(dest->pval,cp);
            }

        }
    }
    return;
}

char *s_val_ptr(char *line, const char *key)
{
    char *retval=NULL;
    if(NULL!=line && *line!='#'){
        char *pk = strstr(line,key);
         if(NULL!=pk){
           char *pd = strstr(pk+strlen(key),CFG_KEYVAL_DEL);
           if(NULL!=pd){
               retval = pd+strlen(CFG_KEYVAL_DEL);
               while(NULL!=retval && isspace(*retval) && *retval!='\0' )retval++;
           }// else line doesn't contain delimiter
        }// else line doesn't contain key
    }// else invalid arg
    return retval;
}

return_code_t config_load(char *src)
{
    return_code_t retval = ERR_NO_IMPL;

    char *path = (NULL==src ? SYS_CONFIG_DFL : src);
    char *linebuf = palloc(CFG_LINE_BYTES);

#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
    FILE_T file_s, *fp=&file_s;
#else
    FILE_T *fp;
#endif
    DPRINT("opening[%s]\r\n",path);
    if(NULL!=linebuf && iow_fopen(&fp, path, IOW_READ)==0){
        char *pval=NULL;
        config_item_t *pitem=config_table;
        ZERO_MEM(linebuf,CFG_LINE_BYTES);
        while (iow_fgets(fp,linebuf, CFG_LINE_BYTES)!=NULL) {
            MPRINT(MOD_CFG,"read line [%s]\r\n",linebuf);
            pitem=config_table;
            while(NULL!=pitem->key){
                pval = s_val_ptr(linebuf, pitem->key);
                if (NULL != pval) {
                    char *pe=pval+strlen(pval)-1;
                    // trim trailing whitespace (CRLF)
                    MPRINT(MOD_CFG,"pval[%s/%p/%d] pe[%p/%02x]\r\n",pval,pval,strlen(pval),pe,*pe);
                   while(isspace(*pe) && pe>pval){
                       MPRINT(MOD_CFG,".");
                            *pe='\0';
                            pe--;
                    }
                    MPRINT(MOD_CFG,"parsing item[%s]\r\n",pitem->key);
                    s_parse_item(pitem, pval);
                    break;
                }
                pitem++;
            }
            ZERO_MEM(linebuf,CFG_LINE_BYTES);
         }
        retval=ERR_SUCCESS;
        iow_fclose(fp);
    }else{
     // could not open file
       EPRINT("file open failed [%s] line[%p]",path,linebuf);
    }
   pfree(linebuf);

  return retval;
}

return_code_t config_export(char *file_path)
{
    return_code_t retval = ERR_INVALID;
    char *line=(char *)palloc(CFG_LINE_BYTES);
    char *path=(NULL==file_path ? SYS_CONFIG_DFL : file_path);
     int test=0;

    if(NULL!=line){

#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
    FILE_T file_s, *fp=&file_s;
#else
    FILE_T *fp;
#endif

       if ( (test=iow_fopen(&fp, path, IOW_WRITE|IOW_CREATE_ALWAYS)) == 0 ) {
            config_item_t *pitem=config_table;
            while(NULL!=pitem->key && NULL!=pitem->pval){
                sprintf(line,"%s%s%s\r\n",pitem->key,CFG_KEYVAL_DEL,
                        pitem->funcs->string(NULL,cfg_fmt_strings[pitem->type],pitem->pval));
                iow_fputs(fp,line);
                pitem++;
            }
             //truncate file needed?
            iow_fclose(fp);
            retval = ERR_SUCCESS;
        }else{
            retval=ERR_FILEIO;
            EPRINT("FOPEN failed [%s/%d]",path,test);
        }

        // no fflush implementation for FatFS
        iow_fclose(fp);
        pfree(line);
    }else{
            retval=ERR_FILEIO;
            EPRINT("palloc failed %s",path);
        }
    return retval;
}

return_code_t config_set(const char *key, void *pval)
{
    return_code_t retval = ERR_INVALID;

    int idx=0,*pn=&idx;

    if(NULL!=key && NULL!=pval){
        config_item_t *pitem=NULL;
        if( (pitem=s_lookup_item(key))!=NULL ){
            if(pitem->type==CTI_STRING){
                s_assign_string(&pitem->pval,pval);
            }else if(NULL!=pitem->funcs->assign){
                pitem->funcs->assign(pitem->pval,pval);
                retval=ERR_SUCCESS;
            }
        }else if(CX_ISVALID(key,pn) ){
                cfg_cx_mem[idx] = *((uint32_t *)pval);
                retval=ERR_SUCCESS;
        }else if(DX_ISVALID(key,pn) ){
                cfg_dx_mem[idx] = *((double *)pval);
                retval=ERR_SUCCESS;
        }else if(FX_ISVALID(key,pn) ){
                cfg_fx_mem = (*((bool *)pval)==0?0:1)<<idx;
                retval=ERR_SUCCESS;
        }

    }// else invalid arg
    return retval;
}

return_code_t config_parse(const char *key, char *str)
{
    return_code_t retval = ERR_INVALID;
    if(NULL!=key && NULL!=str){
        config_item_t *pitem=NULL;
//        bool f=false;
//        uint32_t c=0;

        if( (pitem=s_lookup_item(key))!=NULL ){
            s_parse_item(pitem,str);
            retval=ERR_SUCCESS;
        }else if(config_parse_cx(key,str)==ERR_SUCCESS){
            retval=ERR_SUCCESS;
        }else if(config_parse_dx(key,str)==ERR_SUCCESS){
            retval=ERR_SUCCESS;
        }else if(config_parse_fx(key,str)==ERR_SUCCESS){
            retval=ERR_SUCCESS;
        }
    }// else invalid arg
    return retval;
}

return_code_t config_get(const char *key, void *dest)
{
    return_code_t retval = ERR_NO_IMPL;
    bool f=false;
    uint32_t c=0;
    double d=0;
      if(NULL!=key && NULL!=dest){
        config_item_t *pitem=NULL;
        if( (pitem=s_lookup_item(key))!=NULL  ){
            if(NULL!=pitem->funcs->assign){
                if(pitem->type==CTI_STRING){
                    pitem->funcs->assign(&dest, pitem->pval);
                }else{
                    pitem->funcs->assign(dest, pitem->pval);
                }
                retval=ERR_SUCCESS;
            }
        }else if(config_get_cx(key,&c)==ERR_SUCCESS){
            s_assign_u32(dest,&c);
            retval=ERR_SUCCESS;

        }else if(config_get_dx(key,&d)==ERR_SUCCESS){
            s_assign_double(dest,&d);
            retval=ERR_SUCCESS;

        }else if(config_get_fx(key, &f)==ERR_SUCCESS){
            s_assign_bool(dest,&f);
            retval=ERR_SUCCESS;
        }

    }// else invalid arg

    return retval;
}

bool config_contains(const char *key)
{
    bool retval = false;
    if(NULL!=key){
        uint32_t c=-1;
        double d=0.0;
        bool f=false;
        if(config_get_cx(strstr(key,"CX"),&c)==ERR_SUCCESS){
            retval=true;
        }else if(config_get_dx(strstr(key,"DX"),&d)==ERR_SUCCESS){
            retval=true;
        }else if(config_get_fx(strstr(key,"FX"),&f)==ERR_SUCCESS){
            retval=true;
        }else{
            retval=( NULL!=s_lookup_item(key) ? true : false);
        }
    }
    return retval;
}

config_type_id config_type(const char *key)
{
    config_type_id retval = CTI_INVALID;
    uint32_t c=-1;
    bool f=false;
    if(NULL!=key){
        config_item_t *pitem=s_lookup_item(key);
        if( pitem!=NULL  ){
            retval = pitem->type;
        }else if(config_get_cx(strstr(key,"CX"),&c)==ERR_SUCCESS){
            retval=CTI_U32;
        }else if(config_get_cx(strstr(key,"DX"),&c)==ERR_SUCCESS){
            retval=CTI_DOUBLE;
        }else if(config_get_fx(strstr(key,"FX"),&f)==ERR_SUCCESS){
            retval=CTI_BOOL;
        }
    }
    return retval;
}

return_code_t config_bool_val(const char *key,  bool *dest)
{
    return_code_t retval = ERR_INVALID;

   if(NULL!=key){
        config_item_t *pitem=s_lookup_item(key);
        uint32_t c=-1;
        double d=0.0;
        bool f=false;
        if( pitem!=NULL  ){
            *dest = pitem->funcs->bool_val(pitem->pval);
             retval=ERR_SUCCESS;
       }else if(config_get_cx(strstr(key,"CX"),&c)==ERR_SUCCESS){
            *dest = (c==0 ? true : false);
            retval=ERR_SUCCESS;
        }else if(config_get_dx(strstr(key,"DX"),&d)==ERR_SUCCESS){
            *dest = (d==0.0 ? true : false);
            retval=ERR_SUCCESS;
        }else if(config_get_fx(strstr(key,"FX"),&f)==ERR_SUCCESS){
            *dest = f;
            retval=ERR_SUCCESS;
        }else{
           // error
        }
    }else{
        // error
    }
    return retval;
}

return_code_t config_str_val(const char *key, char **dest, char *alt_fmt)
{
    return_code_t retval=ERR_INVALID;

//    DPRINT("%s - key[%s] dest[%p]\r\n",__FUNCTION__,key,dest);

   if(NULL!=key && NULL!=dest){
       if(NULL==*dest){
           *dest=g_cfg_strval;
       }
        config_item_t *pitem=s_lookup_item(key);
        uint32_t c=-1;
        double d=0.0;
        bool f=false;

        if( NULL!=pitem ){
            pitem->funcs->string(*dest,(NULL!=alt_fmt? alt_fmt : cfg_fmt_strings[pitem->type]),pitem->pval);
            retval=ERR_SUCCESS;
       }else if(config_get_cx(strstr(key,"CX"),&c)==ERR_SUCCESS){
            s_str_u32(*dest,(NULL!=alt_fmt? alt_fmt : cfg_fmt_strings[CTI_U32]),&c);
            retval=ERR_SUCCESS;
        }else if(config_get_dx(strstr(key,"DX"),&d)==ERR_SUCCESS){
            s_str_double(*dest,(NULL!=alt_fmt? alt_fmt : cfg_fmt_strings[CTI_DOUBLE]),&d);
            retval=ERR_SUCCESS;
        }else if(config_get_fx(strstr(key,"FX"),&f)==ERR_SUCCESS){
             s_str_bool(*dest,cfg_fmt_strings[CTI_BOOL],&f);
            retval=ERR_SUCCESS;
        }
    }
    return retval;
}

return_code_t config_double_val(const char *key,  double *dest)
{
    return_code_t  retval = ERR_INVALID;

    if(NULL!=key){
        config_item_t *pitem=s_lookup_item(key);
        uint32_t c=-1;
        double d=0.0;
        bool f=false;
        if( pitem!=NULL && NULL!=pitem->funcs->double_val ){
            *dest  = pitem->funcs->double_val(pitem->pval);
            retval = ERR_SUCCESS;
        }else if(config_get_cx(strstr(key,"CX"),&c)==ERR_SUCCESS){
            *dest = c;
            retval=ERR_SUCCESS;
        }else if(config_get_dx(strstr(key,"DX"),&d)==ERR_SUCCESS){
            *dest = d;
            retval=ERR_SUCCESS;
        }else if(config_get_fx(strstr(key,"FX"),&f)==ERR_SUCCESS){
            *dest = (f ? 1.0 : 0.0);
            retval=ERR_SUCCESS;
        }else{
           // error
        }
    }else{
        // error
    }
    return retval;
}
return_code_t config_int_val(const char *key,  int64_t *dest)
{
    return_code_t  retval = ERR_INVALID;

    if(NULL!=key){
        config_item_t *pitem=s_lookup_item(key);
        uint32_t c=-1;
        double d=0.0;
        bool f=false;
        if( pitem!=NULL && NULL!=pitem->funcs->double_val ){
            *dest  = (int64_t)(pitem->funcs->double_val(pitem->pval));
            retval = ERR_SUCCESS;
        }else if(config_get_cx(strstr(key,"CX"),&c)==ERR_SUCCESS){
            *dest = (int64_t)c;
            retval=ERR_SUCCESS;
        }else if(config_get_dx(strstr(key,"DX"),&d)==ERR_SUCCESS){
            *dest = (int64_t)d;
            retval=ERR_SUCCESS;
        }else if(config_get_fx(strstr(key,"FX"),&f)==ERR_SUCCESS){
            *dest = (f ? 1LL : 0LL);
            retval=ERR_SUCCESS;
        }else{
           // error
        }
    }else{
        // error
    }
    return retval;
}

const char *config_type_str(const char *key)
{
     const char *retval=NULL;
//    DPRINT("%s - key[%s]\r\n",__FUNCTION__,key);
    if(NULL!=key){
        config_item_t *pitem=s_lookup_item(key);
        uint32_t c=-1;
        double d=0.0;
        bool f=false;
        if( NULL!=pitem  ){
            retval = cfg_type_strings[pitem->type];
        }else if(config_get_cx(strstr(key,"CX"),&c)==ERR_SUCCESS){
            retval=cfg_type_strings[CTI_U32];
        }else if(config_get_dx(strstr(key,"DX"),&d)==ERR_SUCCESS){
            retval=cfg_type_strings[CTI_DOUBLE];
        }else if(config_get_fx(strstr(key,"FX"),&f)==ERR_SUCCESS){
            retval=cfg_type_strings[CTI_BOOL];
        }
    }
    return retval;
}

return_code_t config_parse_cx(const char *key, char *value)
{
    return_code_t retval=ERR_INVALID;
    int idx=-1;
    uint32_t val=0;
    if(NULL!=key && NULL!=value){
        if( sscanf(key,"CX%d",&idx)==1 && sscanf(value,"%lu",&val)==1){
            if(idx>=0 && idx<CFG_CX_COUNT){
               cfg_cx_mem[idx]=val;
               retval=ERR_SUCCESS;
             }
        }
    }
    return retval;
}

return_code_t config_parse_dx(const char *key, char *value)
{
    return_code_t retval=ERR_INVALID;
    int idx=-1;
    double val=0;
    if(NULL!=key && NULL!=value){
        if( sscanf(key,"DX%d",&idx)==1 && sscanf(value,"%lf",&val)==1){
            if(idx>=0 && idx<CFG_DX_COUNT){
               cfg_dx_mem[idx]=val;
               retval=ERR_SUCCESS;
             }
        }
    }
    return retval;
}

return_code_t config_parse_fx(const char *key, char *value)
{
    return_code_t retval=ERR_INVALID;
    int idx=-1;
    uint32_t val=0;
    if(NULL!=key && NULL!=value){
        if( sscanf(key,"FX%d",&idx)==1 && sscanf(value,"%lu",&val)==1){
            if(idx>=0 && idx<32){
               if(val==0){
                   cfg_fx_mem &= ~(1<<idx);
               }else{
                   cfg_fx_mem |= (1<<idx);
               }
              retval=ERR_SUCCESS;
            }
        }
    }
    return retval;
}

return_code_t config_get_cx(const char *key, uint32_t *value)
{
    return_code_t retval=ERR_INVALID;
    int idx=-1;
    if(NULL!=key && NULL!=value){

     if( sscanf(key,"CX%d",&idx)==1){
        if(idx>=0 && idx<CFG_CX_COUNT){
           *value=cfg_cx_mem[idx];
           retval = ERR_SUCCESS;
         }
    }
    }
    return retval;
}

return_code_t config_get_dx(const char *key, double *value)
{
    return_code_t retval=ERR_INVALID;
    int idx=-1;
    if(NULL!=key && NULL!=value){
     if( sscanf(key,"DX%d",&idx)==1){
        if(idx>=0 && idx<CFG_DX_COUNT){
           *value=cfg_dx_mem[idx];
           retval = ERR_SUCCESS;
         }
    }
    }
    return retval;
}

return_code_t config_get_fx(const char *key, bool *value)
{
    return_code_t retval=ERR_INVALID;
    int idx=-1;
    if(NULL!=key && NULL!=value){
    if( sscanf(key,"FX%d",&idx)==1 ){
        if(idx>=0 && idx<32){
           *value = ( (cfg_fx_mem&(1<<idx))==0 ? false : true );
           retval = ERR_SUCCESS;
         }
    }
    }
    return retval;
}
uint32_t config_cx_val(int idx)
{
    uint32_t retval=0;
    if(idx>=0 && idx<CFG_CX_COUNT){
           retval=cfg_cx_mem[idx];
    }
    return retval;
}
bool config_fx_val(int idx)
{
    bool retval=0;
    if(idx>=0 && idx<CFG_FX_COUNT){
           retval= ( (cfg_fx_mem&(1<<idx))==0 ? false : true);
    }
    return retval;
}
double config_dx_val(int idx)
{
    double retval=0;
    if(idx>=0 && idx<CFG_DX_COUNT){
           retval=cfg_dx_mem[idx];
    }
    return retval;
}

int config_mnem2flag(void *dest, char *src, const char *mnem)
{
    int retval=-1;
    // src is a config string ("mnem|mnem|..."))
    // dest is a pointer to licor_cflag_t
    if(NULL!=src && NULL!=dest && toupper(src[0])!='X' && !isdigit(src[0])){

        retval=0;
        char *tok = strtok(src,"|");
        const char *cp=mnem;
        int i=0;
        int klen=0;
        uint32_t flag = 0, *pdest=(uint32_t *)dest;
//        DPRINT("src[%s] mnem[%s]\r\n",src,mnem);
        while(tok!=NULL){
            i=0;
            cp=mnem;
            klen=strlen(tok);
            while(*cp!='\0'){
                if(strncmp(tok,cp,klen)==0){
//               DPRINT("  tok[%3s] klen[%d] i[%d] ",tok,klen,i);
                   break;
                }

                i++;
                while(*cp!='|' && *cp!='\0')cp++;

                cp++;
            }
            if(*cp!='\0'){
               flag |= (1ul<<i);
//                DPRINT("flag[%08lX]\r\n",flag);
            }else{
                // not found, add error
                retval++;
            }

            tok=strtok(NULL,"|");
        }
        if(retval==0){
            *pdest = flag;
        }
    }
    return (retval==0?0:-1);
}

int config_flag2mnem(char *dest, void *src, size_t len, const char *mnem, unsigned max_fields)
{
    int retval=-1;
    if(NULL!=dest && NULL!=src && NULL!=mnem && max_fields<=32){
        int i=0;
        char *pd = dest;
        uint32_t flags=*((uint32_t *)src);
        char *mcpy = (char *)pstrdup(mnem);
        if(NULL!=mcpy){
        char *toks[max_fields];
        ZERO_MEM(toks,max_fields);
        int ntoks=0;
        if( (ntoks=tokenize(mcpy, toks, "|", max_fields))<max_fields){
            WMSG("ntoks<max_fields");
        }
        DPRINT("flags[%08lX] ntoks[%d] max_fields[%u] len[%lu]\r\n",flags,ntoks,max_fields,(unsigned long)len);

        for(i=0;i<max_fields;i++){
            if( (pd+strlen(toks[i]+1)) > (dest+len))break;

            if( ((1ul<<i)&flags)!=0){
                if(i>0){
                    sprintf(pd++,"|");
                }
                sprintf(pd,"%s",toks[i]);
                pd+=strlen(toks[i]);
            }
        }

        if(i>=32){
            retval=0;
        }
        pfree(mcpy);
        }
    }
    return retval;
}

//size_t config_size(const char *key)
//{
//    size_t retval = -1;
//    return retval;
//}
//
//uint32_t config_u32val(const char *key)
//{
//    uint32_t retval = 0xffffffff;
//    return retval;
//}
//
//int32_t config_i32val(const char *key)
//{
//    int32_t retval = -1;
//    return retval;
//}
//
//uint16_t config_u16val(const char *key)
//{
//    uint16_t retval = 0xffff;
//    return retval;
//}
//
//int16_t config_i16val(const char *key)
//{
//    int16_t retval = -1;
//    return retval;
//}
//
//double config_dval(const char *key)
//{
//    double retval = -1;
//    return retval;
//}
//
//bool config_bval(const char *key)
//{
//    bool retval = false;
//    return retval;
//}
//
//char *config_sval(const char *key)
//{
//    char * retval = NULL;
//    return retval;
//}
