diff --git a/board/Linux_Posix/ini_demo/CMakeLists.txt b/board/Linux_Posix/ini_demo/CMakeLists.txt
new file mode 100755
index 00000000..00225f52
--- /dev/null
+++ b/board/Linux_Posix/ini_demo/CMakeLists.txt
@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 3.8)
+
+project(ini_test)
+
+set(CMAKE_BUILD_TYPE "Debug")
+set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
+set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
+
+set(TINY_ROOT ../../../)
+
+include_directories(${TINY_ROOT}/osal/cmsis_os)
+include_directories(${TINY_ROOT}/kernel/core/include)
+include_directories(${TINY_ROOT}/kernel/evtdrv/include)
+include_directories(${TINY_ROOT}/kernel/hal/include)
+include_directories(${TINY_ROOT}/kernel/pm/include)
+
+set(CMSIS_SRCS ${TINY_ROOT}/osal/cmsis_os/cmsis_os.c)
+aux_source_directory(${TINY_ROOT}/kernel/core CORE_SRCS)
+aux_source_directory(${TINY_ROOT}/kernel/evtdrv EVTDRV_SRCS)
+aux_source_directory(${TINY_ROOT}/kernel/pm PM_SRCS)
+
+set(ARCH_ROOT ${TINY_ROOT}/arch/linux)
+
+include_directories(${ARCH_ROOT}/common/include)
+include_directories(${ARCH_ROOT}/posix/gcc)
+include_directories(${TINY_ROOT}/components/utils/INI/include)
+
+aux_source_directory(${ARCH_ROOT}/common ARCH_COMMON_SRCS)
+aux_source_directory(${ARCH_ROOT}/posix/gcc ARCH_POSIX_SRCS)
+
+aux_source_directory(${TINY_ROOT}/components/utils/INI/src INI_SRCS)
+
+set(ARCH_SRCS ${ARCH_COMMON_SRCS} ${ARCH_POSIX_SRCS})
+
+set(TINY_SRCS ${ARCH_SRCS} ${CMSIS_SRCS} ${EVTDRV_SRCS} ${PM_SRCS} ${CORE_SRCS} ${INI_SRCS} )
+
+include_directories(./)
+include_directories(./inc)
+
+set(APP_SRCS src/main.c)
+
+add_executable(inidemo ${APP_SRCS} ${TINY_SRCS})
+
+target_link_libraries(inidemo pthread)
\ No newline at end of file
diff --git a/board/Linux_Posix/ini_demo/Makefile b/board/Linux_Posix/ini_demo/Makefile
new file mode 100755
index 00000000..a8c4ff01
--- /dev/null
+++ b/board/Linux_Posix/ini_demo/Makefile
@@ -0,0 +1,50 @@
+###################################################################
+#automatic detection QTOP and LOCALDIR
+CUR_DIR := $(patsubst %/,%,$(dir $(realpath $(firstword $(MAKEFILE_LIST)))))
+TRYQTOP := $(shell if [ -n "$$QTOP" ] ; then\
+ echo $$QTOP;\
+ else\
+ cd $(CUR_DIR); while /usr/bin/test ! -e qmk ; do \
+ dir=`cd ../;pwd`; \
+ if [ "$$dir" = "/" ] ; then \
+ echo Cannot find QTOP in $(firstword $(MAKEFILE_LIST)) 1>&2; \
+ exit 1; \
+ fi ; \
+ cd $$dir; \
+ done ; \
+ pwd; \
+ fi)
+QTOP ?= $(realpath ${TRYQTOP})
+
+ifeq ($(QTOP),)
+$(error Please run this in a tree)
+endif
+LOCALDIR = $(patsubst %/,%,$(subst $(realpath $(QTOP))/,,$(CUR_DIR)))
+export QTOP
+
+####################################################################
+
+
+export BP=Linux_Posix
+
+TREE_LIB_ENABLE=1
+lib=
+subdirs =
+
+
+all::
+ make -C ${QTOP}/arch BP=${BP}
+ make -C ${QTOP}/kernel BP=${BP}
+ make -C ${QTOP}/osal BP=${BP}
+ make -C ${QTOP}/net BP=${BP}
+ make -C ${QTOP}/devices BP=${BP}
+exec =
+LD_A_FILES += $(LIBDIR)/libarch.a
+LD_A_FILES += $(LIBDIR)/libkernel.a
+LD_A_FILES += $(LIBDIR)/libini_demo.a
+LD_A_FILES += $(LIBDIR)/libcmsis_os.a
+LDFLAGS += -lpthread
+
+include ${QTOP}/qmk/generic/Make.exec
+
+
diff --git a/board/Linux_Posix/ini_demo/inc/lwipopts.h b/board/Linux_Posix/ini_demo/inc/lwipopts.h
new file mode 100755
index 00000000..7e9e2522
--- /dev/null
+++ b/board/Linux_Posix/ini_demo/inc/lwipopts.h
@@ -0,0 +1,236 @@
+/**
+ ******************************************************************************
+ * @file lwipopts.h
+ * @author MCD Application Team
+ * @version V1.1.0
+ * @date 31-July-2013
+ * @brief lwIP Options Configuration.
+ * This file is based on Utilities\lwip_v1.4.1\src\include\lwip\opt.h
+ * and contains the lwIP configuration for the STM32F4x7 demonstration.
+ ******************************************************************************
+ * @attention
+ *
+ *
© COPYRIGHT 2013 STMicroelectronics
+ *
+ * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.st.com/software_license_agreement_liberty_v2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************
+ */
+
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+/**
+ * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
+ * critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#define SYS_LIGHTWEIGHT_PROT 1
+
+/**
+ * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
+ * use lwIP facilities.
+ */
+#define NO_SYS 0
+
+/**
+ * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
+ * Mainly for compatibility to old versions.
+ */
+#define NO_SYS_NO_TIMERS 0
+
+/* ---------- Memory options ---------- */
+/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
+ lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
+ byte alignment -> define MEM_ALIGNMENT to 2. */
+#define MEM_ALIGNMENT 4
+
+/* MEM_SIZE: the size of the heap memory. If the application will send
+a lot of data that needs to be copied, this should be set high. */
+#define MEM_SIZE (5 * 1024)
+
+/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
+ sends a lot of data out of ROM (or other static memory), this
+ should be set high. */
+#define MEMP_NUM_PBUF 25
+/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ per active UDP "connection". */
+#define MEMP_NUM_UDP_PCB 4
+/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
+ connections. */
+#define MEMP_NUM_TCP_PCB 6
+/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
+ connections. */
+#define MEMP_NUM_TCP_PCB_LISTEN 6
+/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
+ segments. */
+#define MEMP_NUM_TCP_SEG 150
+/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
+ timeouts. */
+#define MEMP_NUM_SYS_TIMEOUT 6
+
+/* ---------- Pbuf options ---------- */
+/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
+#define PBUF_POOL_SIZE 25
+/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
+#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS + 40 + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN)
+
+/* ---------- TCP options ---------- */
+#define LWIP_TCP 1
+#define TCP_TTL 255
+
+/* Controls if TCP should queue segments that arrive out of
+ order. Define to 0 if your device is low on memory. */
+#define TCP_QUEUE_OOSEQ 0
+
+/* TCP Maximum segment size. */
+#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
+
+/* TCP sender buffer space (bytes). */
+#define TCP_SND_BUF (7 * TCP_MSS)
+
+/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */
+
+#define TCP_SND_QUEUELEN (8 * TCP_SND_BUF / TCP_MSS)
+
+/* TCP receive window. */
+#define TCP_WND (9 * TCP_MSS)
+
+/* ---------- ICMP options ---------- */
+#define LWIP_ICMP 1
+
+/* ---------- DHCP options ---------- */
+/* Define LWIP_DHCP to 1 if you want DHCP configuration of
+ interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
+ turning this on does currently not work. */
+#define LWIP_DHCP 1
+
+/* ---------- UDP options ---------- */
+#define LWIP_UDP 1
+#define UDP_TTL 255
+
+/* ---------- Statistics options ---------- */
+#define LWIP_STATS 0
+#define LWIP_PROVIDE_ERRNO 1
+
+/* ---------- link callback options ---------- */
+/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
+ * whenever the link changes (i.e., link down)
+ */
+#define LWIP_NETIF_LINK_CALLBACK 0
+/*
+ --------------------------------------
+ ---------- Checksum options ----------
+ --------------------------------------
+*/
+
+/*
+The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
+ - To use this feature let the following define uncommented.
+ - To disable it and process by CPU comment the the checksum.
+*/
+#define CHECKSUM_BY_HARDWARE
+//#undef CHECKSUM_BY_HARDWARE
+
+#ifdef CHECKSUM_BY_HARDWARE
+/* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
+#define CHECKSUM_GEN_IP 0
+/* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
+#define CHECKSUM_GEN_UDP 0
+/* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
+#define CHECKSUM_GEN_TCP 0
+/* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
+#define CHECKSUM_CHECK_IP 0
+/* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
+#define CHECKSUM_CHECK_UDP 0
+/* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
+#define CHECKSUM_CHECK_TCP 0
+/* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
+#define CHECKSUM_GEN_ICMP 0
+#else
+/* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
+#define CHECKSUM_GEN_IP 1
+/* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
+#define CHECKSUM_GEN_UDP 1
+/* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
+#define CHECKSUM_GEN_TCP 1
+/* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
+#define CHECKSUM_CHECK_IP 1
+/* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
+#define CHECKSUM_CHECK_UDP 1
+/* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
+#define CHECKSUM_CHECK_TCP 1
+/* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
+#define CHECKSUM_GEN_ICMP 1
+#endif
+
+#define LWIP_TCPIP_CORE_LOCKING 1
+
+/*
+ ----------------------------------------------
+ ---------- Sequential layer options ----------
+ ----------------------------------------------
+*/
+/**
+ * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+ */
+#define LWIP_NETCONN 1
+
+/*
+ ------------------------------------
+ ---------- Socket options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+ */
+#define LWIP_SOCKET 1
+
+/*
+ ---------------------------------
+ ---------- OS options ----------
+ ---------------------------------
+*/
+
+#define DEFAULT_UDP_RECVMBOX_SIZE 10
+#define DEFAULT_TCP_RECVMBOX_SIZE 10
+#define DEFAULT_ACCEPTMBOX_SIZE 10
+#define DEFAULT_THREAD_STACKSIZE 1024 * 2
+
+#define TCPIP_THREAD_NAME "lwip"
+#define TCPIP_THREAD_STACKSIZE 1024
+#define TCPIP_MBOX_SIZE 10
+#define TCPIP_THREAD_PRIO 1
+
+#define LWIP_DNS_API_DECLARE_STRUCTS 1
+#define LWIP_DNS 1
+
+/** DNS server IP address */
+#ifndef DNS_SERVER_ADDRESS
+#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
+#endif
+
+/*
+ ----------------------------------------
+ ---------- Lwip Debug options ----------
+ ----------------------------------------
+*/
+#define LWIP_DEBUG 0
+
+#define ethernet_with_mac 1
+
+#endif /* __LWIPOPTS_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/board/Linux_Posix/ini_demo/inc/tos_config.h b/board/Linux_Posix/ini_demo/inc/tos_config.h
new file mode 100755
index 00000000..4c75d6e4
--- /dev/null
+++ b/board/Linux_Posix/ini_demo/inc/tos_config.h
@@ -0,0 +1,51 @@
+#ifndef _TOS_CONFIG_H_
+#define _TOS_CONFIG_H_
+
+#include "stddef.h"
+#include "stdint.h"
+
+#define TOS_CFG_TASK_PRIO_MAX 10u // 配置TencentOS tiny默认支持的最大优先级数量
+
+#define TOS_CFG_ROUND_ROBIN_EN 1u // 配置TencentOS tiny的内核是否开启时间片轮转
+
+#define TOS_CFG_OBJECT_VERIFY_EN 1u // 配置TencentOS tiny是否校验指针合法
+
+#define TOS_CFG_EVENT_EN 1u // TencentOS tiny 事件模块功能宏
+
+#define TOS_CFG_MMHEAP_EN 1u // 配置TencentOS tiny是否开启动态内存模块
+
+#define TOS_CFG_MMHEAP_POOL_SIZE 0x100 // 配置TencentOS tiny动态内存池大小
+
+#define TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE 0x100 // 配置TencentOS tiny动态内存池大小
+
+#define TOS_CFG_MUTEX_EN 1u // 配置TencentOS tiny是否开启互斥锁模块
+
+#define TOS_CFG_MESSAGE_QUEUE_EN 1u
+#define TOS_CFG_MAIL_QUEUE_EN 1u
+
+#define TOS_CFG_PRIORITY_MESSAGE_QUEUE_EN 1u
+
+#define TOS_CFG_PRIORITY_MAIL_QUEUE_EN 1u
+#define TOS_CFG_TIMER_EN 1u // 配置TencentOS tiny是否开启软件定时器模块
+
+#define TOS_CFG_SEM_EN 1u // 配置TencentOS tiny是否开启信号量模块
+
+#define TOS_CFG_MMBLK_EN 1u
+
+#if (TOS_CFG_QUEUE_EN > 0u)
+#define TOS_CFG_MSG_EN 1u
+#else
+#define TOS_CFG_MSG_EN 0u
+#endif
+
+#define TOS_CFG_MSG_POOL_SIZE 10u // 配置TencentOS tiny消息队列大小
+
+#define TOS_CFG_IDLE_TASK_STK_SIZE 256u // 配置TencentOS tiny空闲任务栈大小
+
+#define TOS_CFG_CPU_TICK_PER_SECOND 1000u // 配置TencentOS tiny的tick频率
+
+#define TOS_CFG_CPU_CLOCK 1000000u // 配置TencentOS tiny CPU频率
+
+#define TOS_CFG_TIMER_AS_PROC 1u // 配置是否将TIMER配置成函数模式
+
+#endif
diff --git a/board/Linux_Posix/ini_demo/readme.md b/board/Linux_Posix/ini_demo/readme.md
new file mode 100755
index 00000000..a35c15da
--- /dev/null
+++ b/board/Linux_Posix/ini_demo/readme.md
@@ -0,0 +1,36 @@
+# How to run the demo in linux
+
+## step1
+make sure your develop environment.
++ `cmake` and version greater than 3.8.2
++ `gcc` `gdb` `make` is installed
+
+## step2
+make `build` directory and compile in `build`
+
+```bash
+mkdir build && cd build
+cmake ..
+make
+```
+
+## step3
+run program !!
+
+```bash
+# in build directory
+./hello_world
+```
+
+## other
+you can copy this demo to other path, but if you want do it,
+you need modify `CMakeLists.txt`. find line
+
+```cmake
+set(TINY_ROOT ../../../)
+```
+
+and modify `path-to-tinyos`
+```cmake
+set(TINY_ROOT path-to-tinyos)
+```
diff --git a/board/Linux_Posix/ini_demo/src/main.c b/board/Linux_Posix/ini_demo/src/main.c
new file mode 100755
index 00000000..3de0de9a
--- /dev/null
+++ b/board/Linux_Posix/ini_demo/src/main.c
@@ -0,0 +1,170 @@
+#include "cmsis_os.h"
+#include "iniparser.h"
+#define CONFIG_NAME "Config.ini"
+
+struct DataInfo_t
+{
+ int InitData;
+ int VolumeData;
+ int LanguageVersion;
+};
+
+#define TASK1_STK_SIZE 512
+void task1(void *arg);
+osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
+
+void task1(void *arg)
+{
+ int Len = -1;
+ int Ret = -1;
+ char Buf[128];
+ char *DataPtr = NULL;
+ struct DataInfo_t Data;
+ FILE *IniTest = NULL ;
+ FILE *DefaultIni = NULL;
+ dictionary *ConfigIni = NULL;
+
+ /*1. Create ini config file*/
+ IniTest = fopen(CONFIG_NAME, "w");
+ if(NULL == IniTest)
+ {
+ printf("IniTest is Null!\n");
+ return ;
+ }
+
+ fprintf(IniTest,
+ "[Setting]\n"
+ "init_data=0;\n"
+ "volume_data=1;\n"
+ "language_version=1;\n"
+ );
+
+ Ret = fclose(IniTest);
+ if(Ret != 0)
+ {
+ printf("close IniTest fail!\n");
+ return ;
+ }
+
+ IniTest = NULL;
+
+ IniTest = fopen(CONFIG_NAME, "r");
+ if(NULL == IniTest)
+ {
+ printf("IniTest is Null!\n");
+ return ;
+ }
+
+ memset(Buf, 0, sizeof(Buf));
+ while(fgets(Buf, sizeof(Buf), IniTest))
+ {
+ printf("Buf: %s", Buf);
+ }
+
+ fclose(IniTest);
+ IniTest = NULL;
+
+ putchar('\n');
+
+ /*2. Test read ini config file data*/
+ ConfigIni = iniparser_load(CONFIG_NAME);
+ if(NULL == ConfigIni)
+ {
+ printf("ConfigIni is NULL!\n");
+ return ;
+ }
+
+ //iniparser_dump(ConfigIni, stderr);
+ Data.InitData = iniparser_getint(ConfigIni,"Setting:init_data",-1);
+ if(-1 == Data.InitData)
+ {
+ printf("iniparser_getint fail!\n");
+ return ;
+ }
+ Data.VolumeData = iniparser_getint(ConfigIni,"Setting:volume_data",-1);
+ if(-1 == Data.VolumeData)
+ {
+ printf("iniparser_getint fail!\n");
+ return ;
+ }
+ Data.LanguageVersion = iniparser_getint(ConfigIni,"Setting:language_version",-1);
+ if(-1 == Data.LanguageVersion)
+ {
+ printf("iniparser_getint fail!\n");
+ return ;
+ }
+
+ printf("Data.InitData:%d\n", Data.InitData);
+ printf("Data.VolumeData:%d\n", Data.VolumeData);
+ printf("Data.LanguageVersion:%d\n", Data.LanguageVersion);
+
+ /* 3. Set modify one of the parameters*/
+ iniparser_set(ConfigIni,"Setting:init_data", "111");
+ iniparser_set(ConfigIni,"Setting:volume_data", "222");
+ iniparser_set(ConfigIni,"Setting:language_version", "333");
+
+ putchar('\n');
+
+ /*4. Write data to int config file*/
+ DefaultIni = fopen(CONFIG_NAME, "w");
+ if(NULL == DefaultIni)
+ {
+ printf("DefaultIni is NULL!\n");
+ return ;
+ }
+ iniparser_dump_ini(ConfigIni, DefaultIni);
+ Ret = fclose(DefaultIni);
+ if(Ret != 0)
+ {
+ printf("close DefaultIni fail!\n");
+ return ;
+ }
+
+ /*5.Read the modified Config ini file data*/
+ Data.InitData = iniparser_getint(ConfigIni, "Setting:init_data", -1);
+ if(-1 == Data.InitData)
+ {
+ printf("iniparser_getint fail!\n");
+ return ;
+ }
+ Data.VolumeData = iniparser_getint(ConfigIni, "Setting:volume_data", -1);
+ if(-1 == Data.VolumeData)
+ {
+ printf("iniparser_getint fail!\n");
+ return ;
+ }
+ Data.LanguageVersion = iniparser_getint(ConfigIni, "Setting:language_version", -1);
+ if(-1 == Data.LanguageVersion)
+ {
+ printf("iniparser_getint fail!\n");
+ return ;
+ }
+
+ printf("Data.InitData:%d\n", Data.InitData);
+ printf("Data.VolumeData:%d\n", Data.VolumeData);
+ printf("Data.LanguageVersion:%d\n", Data.LanguageVersion);
+
+ iniparser_freedict(ConfigIni);
+
+ while(1)
+ {
+ printf("Ini test success!\n");
+ osDelay(1000);
+ }
+}
+
+void application_entry(void *arg)
+{
+ osThreadCreate(osThread(task1), NULL); // Create task1
+}
+
+int main(void)
+{
+ osKernelInitialize(); //TOS Tiny kernel initialize
+ application_entry(NULL);
+ osKernelStart(); //Start TOS Tiny
+
+ while (1)
+ {
+ }
+}
diff --git a/components/utils/INI/Makefile b/components/utils/INI/Makefile
new file mode 100755
index 00000000..69305393
--- /dev/null
+++ b/components/utils/INI/Makefile
@@ -0,0 +1,35 @@
+###################################################################
+#automatic detection QTOP and LOCALDIR
+CUR_DIR := $(patsubst %/,%,$(dir $(realpath $(firstword $(MAKEFILE_LIST)))))
+TRYQTOP := $(shell if [ -n "$$QTOP" ] ; then\
+ echo $$QTOP;\
+ else\
+ cd $(CUR_DIR); while /usr/bin/test ! -d qmk ; do \
+ dir=`cd ../;pwd`; \
+ if [ "$$dir" = "/" ] ; then \
+ echo Cannot find QTOP in $(firstword $(MAKEFILE_LIST)) 1>&2; \
+ exit 1; \
+ fi ; \
+ cd $$dir; \
+ done ; \
+ pwd; \
+ fi)
+QTOP ?= $(realpath ${TRYQTOP})
+
+ifeq ($(QTOP),)
+$(error Please run this in a tree)
+endif
+LOCALDIR = $(patsubst %/,%,$(subst $(realpath $(QTOP))/,,$(CUR_DIR)))
+
+####################################################################
+
+
+TREE_LIB_ENABLE=y
+lib=
+subdirs=
+
+CFGFLAGS += -I$(CUR_DIR)/include
+
+include ${QTOP}/qmk/generic/Make.tpl
+
+
diff --git a/components/utils/INI/include/dictionary.h b/components/utils/INI/include/dictionary.h
new file mode 100755
index 00000000..0835865e
--- /dev/null
+++ b/components/utils/INI/include/dictionary.h
@@ -0,0 +1,207 @@
+/*
+ Copyright (c) 2000-2011 by Nicolas Devillard.
+ MIT License
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.h
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef DICTIONARY_H
+#define DICTIONARY_H
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include
+#include
+#include
+//#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*---------------------------------------------------------------------------
+ New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dictionary object
+
+ This object contains a list of string/string associations. Each
+ association is identified by a unique string key. Looking up values
+ in the dictionary is speeded up by the use of a (hopefully collision-free)
+ hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_
+{
+ int n ; /** Number of entries in dictionary */
+ int size ; /** Storage size */
+ char ** val ; /** List of string values */
+ char ** key ; /** List of string keys */
+ unsigned * hash ; /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+ Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Duplicate a string
+ @param s String to duplicate
+ @return Pointer to a newly allocated string, to be freed with free()
+
+ This is a replacement for strdup(). This implementation is provided
+ for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+char * xstrdup(const char * s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/components/utils/INI/include/iniparser.h b/components/utils/INI/include/iniparser.h
new file mode 100755
index 00000000..4c6a59c6
--- /dev/null
+++ b/components/utils/INI/include/iniparser.h
@@ -0,0 +1,348 @@
+/*
+ Copyright (c) 2000-2011 by Nicolas Devillard.
+ MIT License
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.h
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef INIPARSER_H
+#define INIPARSER_H
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include
+#include
+#include
+
+#ifdef SUPPORT_FATFS
+#include "fatfs.h"
+#endif
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+/* #include */
+
+#include "dictionary.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+char * iniparser_getsecname(dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+#ifdef SUPPORT_FATFS
+void iniparser_dump_ini(dictionary * d, FIL * f);
+#else
+void iniparser_dump_ini(dictionary * d, FILE * f);
+#endif
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+#ifdef SUPPORT_FATFS
+void iniparser_dumpsection_ini(dictionary * d, char * s, FIL *f);
+#else
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
+#endif
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+#ifdef SUPPORT_FATFS
+void iniparser_dump(dictionary * d, FIL *f);
+#else
+void iniparser_dump(dictionary * d, FILE * f);
+#endif
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return pointer to statically allocated character strings
+
+ This function queries a dictionary and finds all keys in a given section.
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/components/utils/INI/src/dictionary.c b/components/utils/INI/src/dictionary.c
new file mode 100755
index 00000000..86cea8ab
--- /dev/null
+++ b/components/utils/INI/src/dictionary.c
@@ -0,0 +1,489 @@
+/*
+ Copyright (c) 2000-2011 by Nicolas Devillard.
+ MIT License
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.c
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include
+#include
+#include
+//#include
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ 1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ 128
+
+/** Invalid key token */
+#define DICT_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private functions
+ ---------------------------------------------------------------------------*/
+
+/* Doubles the allocated size associated to a pointer */
+/* 'size' is the current allocated size. */
+static void * mem_double(void * ptr, size_t size)
+{
+ void * newptr ;
+
+ newptr = calloc(2 * size, 1);
+
+ if (newptr == NULL)
+ {
+ return NULL ;
+ }
+
+ memcpy(newptr, ptr, size);
+ free(ptr);
+ return newptr ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Duplicate a string
+ @param s String to duplicate
+ @return Pointer to a newly allocated string, to be freed with free()
+
+ This is a replacement for strdup(). This implementation is provided
+ for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+char * xstrdup(const char * s)
+{
+ char * t ;
+ size_t len ;
+
+ if (!s)
+ return NULL ;
+
+ len = strlen(s) + 1 ;
+ t = (char *)malloc(len) ;
+
+ if (t)
+ {
+ memcpy(t, s, len) ;
+ }
+
+ return t ;
+}
+
+/*---------------------------------------------------------------------------
+ Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+ size_t len ;
+ unsigned hash ;
+ size_t i ;
+
+ len = strlen(key);
+
+ for (hash = 0, i = 0 ; i < len ; i++)
+ {
+ hash += (unsigned)key[i] ;
+ hash += (hash << 10);
+ hash ^= (hash >> 6) ;
+ }
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size)
+{
+ dictionary * d ;
+
+ /* If no size was specified, allocate space for DICTMINSZ */
+ if (size < DICTMINSZ) size = DICTMINSZ ;
+
+ d = (dictionary *)calloc(1, sizeof * d) ;
+
+ if (d)
+ {
+ d->size = size ;
+ d->val = (char **)calloc(size, sizeof * d->val);
+ d->key = (char **)calloc(size, sizeof * d->key);
+ d->hash = (unsigned int *)calloc(size, sizeof * d->hash);
+ }
+
+ return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+ int i ;
+
+ if (d == NULL) return ;
+
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] != NULL)
+ free(d->key[i]);
+
+ if (d->val[i] != NULL)
+ free(d->val[i]);
+ }
+
+ free(d->val);
+ free(d->key);
+ free(d->hash);
+ free(d);
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def)
+{
+ unsigned hash ;
+ int i ;
+
+ hash = dictionary_hash(key);
+
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] == NULL)
+ continue ;
+
+ /* Compare hash */
+ if (hash == d->hash[i])
+ {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i]))
+ {
+ return d->val[i] ;
+ }
+ }
+ }
+
+ return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+ int i ;
+ unsigned hash ;
+
+ if (d == NULL || key == NULL) return -1 ;
+
+ /* Compute hash for this key */
+ hash = dictionary_hash(key) ;
+
+ /* Find if value is already in dictionary */
+ if (d->n > 0)
+ {
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] == NULL)
+ continue ;
+
+ if (hash == d->hash[i]) /* Same hash value */
+ {
+ if (!strcmp(key, d->key[i])) /* Same key */
+ {
+ /* Found a value: modify and return */
+ if (d->val[i] != NULL)
+ free(d->val[i]);
+
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ /* Value has been modified: return */
+ return 0 ;
+ }
+ }
+ }
+ }
+
+ /* Add a new value */
+ /* See if dictionary needs to grow */
+ if (d->n == d->size)
+ {
+
+ /* Reached maximum size: reallocate dictionary */
+ d->val = (char **)mem_double(d->val, d->size * sizeof * d->val) ;
+ d->key = (char **)mem_double(d->key, d->size * sizeof * d->key) ;
+ d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof * d->hash) ;
+
+ if ((d->val == NULL) || (d->key == NULL) || (d->hash == NULL))
+ {
+ /* Cannot grow dictionary */
+ return -1 ;
+ }
+
+ /* Double size */
+ d->size *= 2 ;
+ }
+
+ /* Insert key in the first empty slot. Start at d->n and wrap at
+ d->size. Because d->n < d->size this will necessarily
+ terminate. */
+ for (i = d->n ; d->key[i] ; )
+ {
+ if(++i == d->size) i = 0;
+ }
+
+ /* Copy key */
+ d->key[i] = xstrdup(key);
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ d->hash[i] = hash;
+ d->n ++ ;
+ return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+ unsigned hash ;
+ int i ;
+
+ if (key == NULL)
+ {
+ return;
+ }
+
+ hash = dictionary_hash(key);
+
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] == NULL)
+ continue ;
+
+ /* Compare hash */
+ if (hash == d->hash[i])
+ {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i]))
+ {
+ /* Found key */
+ break ;
+ }
+ }
+ }
+
+ if (i >= d->size)
+ /* Key not found */
+ return ;
+
+ free(d->key[i]);
+ d->key[i] = NULL ;
+
+ if (d->val[i] != NULL)
+ {
+ free(d->val[i]);
+ d->val[i] = NULL ;
+ }
+
+ d->hash[i] = 0 ;
+ d->n -- ;
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out)
+{
+ int i ;
+
+ if (d == NULL || out == NULL) return ;
+
+ if (d->n < 1)
+ {
+ fprintf(out, "empty dictionary\n");
+ return ;
+ }
+
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i])
+ {
+ fprintf(out, "%20s\t[%s]\n",
+ d->key[i],
+ d->val[i] ? d->val[i] : "UNDEF");
+ }
+ }
+
+ return ;
+}
+
+
+/* Test code */
+#ifdef TESTDIC
+#define NVALS 20000
+int main(int argc, char *argv[])
+{
+ dictionary * d ;
+ char * val ;
+ int i ;
+ char cval[90] ;
+
+ /* Allocate dictionary */
+ printf("allocating...\n");
+ d = dictionary_new(0);
+
+ /* Set values in dictionary */
+ printf("setting %d values...\n", NVALS);
+
+ for (i = 0 ; i < NVALS ; i++)
+ {
+ sprintf(cval, "%04d", i);
+ dictionary_set(d, cval, "salut");
+ }
+
+ printf("getting %d values...\n", NVALS);
+
+ for (i = 0 ; i < NVALS ; i++)
+ {
+ sprintf(cval, "%04d", i);
+ val = dictionary_get(d, cval, DICT_INVALID_KEY);
+
+ if (val == DICT_INVALID_KEY)
+ {
+ printf("cannot get value for key [%s]\n", cval);
+ }
+ }
+
+ printf("unsetting %d values...\n", NVALS);
+
+ for (i = 0 ; i < NVALS ; i++)
+ {
+ sprintf(cval, "%04d", i);
+ dictionary_unset(d, cval);
+ }
+
+ if (d->n != 0)
+ {
+ printf("error deleting values\n");
+ }
+
+ printf("deallocating...\n");
+ dictionary_del(d);
+ return 0 ;
+}
+#endif
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/components/utils/INI/src/iniparser.c b/components/utils/INI/src/iniparser.c
new file mode 100755
index 00000000..4dd8662e
--- /dev/null
+++ b/components/utils/INI/src/iniparser.c
@@ -0,0 +1,1141 @@
+/*
+ Copyright (c) 2000-2011 by Nicolas Devillard.
+ MIT License
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.c
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#include
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ (100)
+#define INI_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_
+{
+ LINE_UNPROCESSED,
+ LINE_ERROR,
+ LINE_EMPTY,
+ LINE_COMMENT,
+ LINE_SECTION,
+ LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Convert a string to lowercase.
+ @param s String to convert.
+
+ This function modifies the string passed, the modified string
+ contains a lowercased version of the input string.
+ */
+/*--------------------------------------------------------------------------*/
+
+static void strlwc(char * s)
+{
+ int i ;
+
+ if (s == NULL) return;
+
+ i = 0 ;
+
+ while (s[i])
+ {
+ s[i] = (char)tolower((int)s[i]);
+ i++ ;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Remove blanks at the beginning and the end of a string.
+ @param s String to parse.
+
+ This function modifies the input string and returns a modified string
+ which is identical to the input string, except that all blank
+ characters at the end and the beg. of the string have been removed.
+ */
+/*--------------------------------------------------------------------------*/
+static void strstrip(char * s)
+{
+ if (s == NULL) return ;
+
+ char *last = s + strlen(s);
+ char *dest = s;
+
+ while (isspace((int)*s) && *s) s++;
+
+ while (last > s)
+ {
+ if (!isspace((int) * (last - 1)))
+ break ;
+
+ last -- ;
+ }
+
+ *last = (char)0;
+
+ memmove(dest, s, last - s + 1);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(dictionary * d)
+{
+ int i ;
+ int nsec ;
+
+ if (d == NULL) return -1 ;
+
+ nsec = 0 ;
+
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] == NULL)
+ continue ;
+
+ if (strchr(d->key[i], ':') == NULL)
+ {
+ nsec ++ ;
+ }
+ }
+
+ return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getsecname(dictionary * d, int n)
+{
+ int i ;
+ int foundsec ;
+
+ if (d == NULL || n < 0) return NULL ;
+
+ foundsec = 0 ;
+
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] == NULL)
+ continue ;
+
+ if (strchr(d->key[i], ':') == NULL)
+ {
+ foundsec++ ;
+
+ if (foundsec > n)
+ break ;
+ }
+ }
+
+ if (foundsec <= n)
+ {
+ return NULL ;
+ }
+
+ return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+#ifdef SUPPORT_FATFS
+void iniparser_dump(dictionary * d, FIL *f)
+#else
+void iniparser_dump(dictionary * d, FILE * f)
+#endif
+{
+ int i ;
+
+ if (d == NULL || f == NULL) return ;
+
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] == NULL)
+ continue ;
+
+ if (d->val[i] != NULL)
+ {
+ #ifdef SUPPORT_FATFS
+ f_printf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+ #else
+ fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+ #endif
+ }
+ else
+ {
+ #ifdef SUPPORT_FATFS
+ f_printf(f, "[%s]=UNDEF\n", d->key[i]);
+ #else
+ fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+ #endif
+ }
+ }
+
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+#ifdef SUPPORT_FATFS
+void iniparser_dump_ini(dictionary * d, FIL *f)
+#else
+void iniparser_dump_ini(dictionary * d, FILE *f)
+#endif
+{
+ int i ;
+ int nsec ;
+ char * secname ;
+
+ if (d == NULL || f == NULL) return ;
+
+ nsec = iniparser_getnsec(d);
+
+ if (nsec < 1)
+ {
+ /* No section in file: dump all keys as they are */
+ for (i = 0 ; i < d->size ; i++)
+ {
+ if (d->key[i] == NULL)
+ continue ;
+
+ #ifdef SUPPORT_FATFS
+ f_printf(f, "%s = %s\n", d->key[i], d->val[i]);
+ #else
+ fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+ #endif
+ }
+
+ return ;
+ }
+
+ for (i = 0 ; i < nsec ; i++)
+ {
+ secname = iniparser_getsecname(d, i) ;
+ iniparser_dumpsection_ini(d, secname, f) ;
+ }
+
+ #ifdef SUPPORT_FATFS
+ f_printf(f, "\n");
+ #else
+ fprintf(f, "\n");
+ #endif
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+#ifdef SUPPORT_FATFS
+void iniparser_dumpsection_ini(dictionary * d, char * s, FIL *f)
+#else
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
+#endif
+{
+ int j ;
+ char *keym;
+ int secsize ;
+
+ if (d == NULL || f == NULL) return ;
+
+ if (! iniparser_find_entry(d, s)) return ;
+
+ #ifdef SUPPORT_FATFS
+ f_printf(f, "\n[%s]\n", s);
+ #else
+ fprintf(f, "\n[%s]\n", s);
+ #endif
+ secsize = (int)strlen(s) + 2;
+ keym = (char *)malloc(secsize);
+ snprintf(keym, secsize, "%s:", s);
+
+ for (j = 0 ; j < d->size ; j++)
+ {
+ if (d->key[j] == NULL)
+ continue ;
+
+ if (!strncmp(d->key[j], keym, secsize - 1))
+ {
+ #ifdef SUPPORT_FATFS
+ f_printf(f,
+ "%-30s = %s\n",
+ d->key[j] + secsize - 1,
+ d->val[j] ? d->val[j] : "");
+ #else
+ fprintf(f,
+ "%-30s = %s\n",
+ d->key[j]+secsize-1,
+ d->val[j] ? d->val[j] : "");
+ #endif
+ }
+ }
+
+ #ifdef SUPPORT_FATFS
+ f_printf(f, "\n");
+ #else
+ fprintf(f, "\n");
+ #endif
+
+ free(keym);
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s)
+{
+ int secsize, nkeys ;
+ char *keym;
+ int j ;
+
+ nkeys = 0;
+
+ if (d == NULL) return nkeys;
+
+ if (! iniparser_find_entry(d, s)) return nkeys;
+
+ secsize = (int)strlen(s) + 2;
+ keym = (char *)malloc(secsize);
+ snprintf(keym, secsize, "%s:", s);
+
+ for (j = 0 ; j < d->size ; j++)
+ {
+ if (d->key[j] == NULL)
+ continue ;
+
+ if (!strncmp(d->key[j], keym, secsize - 1))
+ nkeys++;
+ }
+
+ free(keym);
+ return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return pointer to statically allocated character strings
+
+ This function queries a dictionary and finds all keys in a given section.
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s)
+{
+
+ char **keys;
+
+ int i, j ;
+ char *keym;
+ int secsize, nkeys ;
+
+ keys = NULL;
+
+ if (d == NULL) return keys;
+
+ if (! iniparser_find_entry(d, s)) return keys;
+
+ nkeys = iniparser_getsecnkeys(d, s);
+
+ keys = (char**) malloc(nkeys * sizeof(char*));
+
+ secsize = (int)strlen(s) + 2;
+ keym = (char *)malloc(secsize);
+ snprintf(keym, secsize, "%s:", s);
+
+ i = 0;
+
+ for (j = 0 ; j < d->size ; j++)
+ {
+ if (d->key[j] == NULL)
+ continue ;
+
+ if (!strncmp(d->key[j], keym, secsize - 1))
+ {
+ keys[i] = d->key[j];
+ i++;
+ }
+ }
+
+ free(keym);
+ return keys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
+{
+ char * lc_key ;
+ char * sval ;
+
+ if (d == NULL || key == NULL)
+ return def ;
+
+ lc_key = xstrdup(key);
+ strlwc(lc_key);
+ sval = dictionary_get(d, lc_key, def);
+ free(lc_key);
+ return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ "42" -> 42
+ "042" -> 34 (octal -> decimal)
+ "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+
+ if (str == INI_INVALID_KEY) return notfound ;
+
+ return (int)strtol(str, NULL, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+
+ if (str == INI_INVALID_KEY) return notfound ;
+
+ return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
+{
+ char * c ;
+ int ret ;
+
+ c = iniparser_getstring(d, key, INI_INVALID_KEY);
+
+ if (c == INI_INVALID_KEY) return notfound ;
+
+ if (c[0] == 'y' || c[0] == 'Y' || c[0] == '1' || c[0] == 't' || c[0] == 'T')
+ {
+ ret = 1 ;
+ }
+ else if (c[0] == 'n' || c[0] == 'N' || c[0] == '0' || c[0] == 'f' || c[0] == 'F')
+ {
+ ret = 0 ;
+ }
+ else
+ {
+ ret = notfound ;
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(
+ dictionary * ini,
+ const char * entry
+)
+{
+ int found = 0 ;
+
+ if (iniparser_getstring(ini, entry, INI_INVALID_KEY) != INI_INVALID_KEY)
+ {
+ found = 1 ;
+ }
+
+ return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+ int result = 0;
+ char *lc_entry = xstrdup(entry);
+ strlwc(lc_entry);
+ result = dictionary_set(ini, lc_entry, val) ;
+ free(lc_entry);
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+ char* lc_entry = xstrdup(entry);
+ strlwc(lc_entry);
+ dictionary_unset(ini, lc_entry);
+ free(lc_entry);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Load a single line from an INI file
+ @param input_line Input line, may be concatenated multi-line input
+ @param section Output space to store section
+ @param key Output space to store key
+ @param value Output space to store value
+ @return line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+ int line_size,
+ const char * input_line,
+ char ** section_out,
+ char ** key_out,
+ char ** value_out)
+{
+ line_status sta ;
+ int len = line_size - 1;
+ char * line = (char *)malloc(line_size);
+ char * key = NULL;
+ char * value = NULL;
+ char * equals = NULL;
+
+ if (!line)
+ {
+ fprintf(stderr, "iniparser: memory alloc error\n");
+ return LINE_ERROR;
+ }
+
+ *line = 0;
+
+
+ strcpy(line, input_line);
+ strstrip(line);
+ len = (int)strlen(line);
+
+ /* only allocate necessary space for key & val */
+ equals = strchr(line, '=');
+
+ if (equals)
+ {
+ value = (char *)malloc((len + line) - equals + 1);
+ key = (char *)malloc(equals - line + 1);
+ *value = 0;
+ }
+ else
+ {
+ key = (char *)malloc(line_size + 1);
+ }
+
+ if (!key || (equals && !value))
+ {
+ fprintf(stderr, "iniparser: memory alloc error\n");
+ sta = LINE_ERROR;
+ goto out;
+ }
+
+ *key = 0;
+
+ sta = LINE_UNPROCESSED ;
+
+ if (len < 1)
+ {
+ /* Empty line */
+ sta = LINE_EMPTY ;
+ }
+ else if (line[0] == '#' || line[0] == ';')
+ {
+ /* Comment line */
+ sta = LINE_COMMENT ;
+ }
+ else if (line[0] == '[' && line[len - 1] == ']')
+ {
+ /* Section name */
+ sscanf(line, "[%[^]]", key);
+ strstrip(key);
+ strlwc(key);
+ sta = LINE_SECTION ;
+ *section_out = key;
+ /* don't free key's memory */
+ key = NULL;
+ }
+ else if (equals && (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+ || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
+ || sscanf (line, "%[^=] = %[^;#]", key, value) == 2))
+ {
+ /* Usual key=value, with or without comments */
+ strstrip(key);
+ strlwc(key);
+ strstrip(value);
+
+ /*
+ * sscanf cannot handle '' or "" as empty values
+ * this is done here
+ */
+ if (!strcmp(value, "\"\"") || (!strcmp(value, "''")))
+ {
+ value[0] = 0 ;
+ }
+
+ *key_out = key;
+ *value_out = value;
+ key = NULL;
+ value = NULL;
+ sta = LINE_VALUE ;
+ }
+ else if (equals && (sscanf(line, "%[^=] = %[;#]", key, value) == 2
+ || sscanf(line, "%[^=] %[=]", key, value) == 2))
+ {
+ /*
+ * Special cases:
+ * key=
+ * key=;
+ * key=#
+ */
+ strstrip(key);
+ strlwc(key);
+ value[0] = 0 ;
+ *key_out = key;
+ *value_out = value;
+
+ /* don't free out params key or val's memory */
+ key = NULL;
+ value = NULL;
+ sta = LINE_VALUE ;
+ }
+ else
+ {
+ /* Generate syntax error */
+ sta = LINE_ERROR ;
+ }
+
+out:
+
+ if (line)
+ {
+ free(line);
+ line = NULL;
+ }
+
+ if (key)
+ {
+ free(key);
+ key = NULL;
+ }
+
+ if (value)
+ {
+ free(value);
+ value = NULL;
+ }
+
+ return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+ char line [ASCIILINESZ + 1] ;
+ char *section = xstrdup("");
+ char *current_section = NULL;
+ char *key = NULL;
+ char *val = NULL;
+ char* full_line = NULL;
+ char* prev_line = NULL;
+
+ int len ;
+ int lineno = 0 ;
+ int errs = 0;
+ int seckey_size = 0;
+
+ #ifndef SUPPORT_FATFS
+ FILE * in = NULL ;
+ #endif
+
+ dictionary * dict = NULL ;
+
+ #ifdef SUPPORT_FATFS
+ retUSER_SYS_CONFIG = f_open(&USER_SYS_CONFIG_File, ininame, FA_OPEN_EXISTING | FA_READ);
+ if(FR_OK != retUSER_SYS_CONFIG)
+ {
+ fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+ goto out ;
+ }
+ #else
+
+ if ((in=fopen(ininame, "r"))==NULL) {
+ fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+ goto out;
+ }
+ #endif
+
+ dict = dictionary_new(0) ;
+
+ if (!dict)
+ {
+ goto out;
+ }
+
+ memset(line, 0, ASCIILINESZ);
+
+ #ifdef SUPPORT_FATFS
+ while(f_gets(line, 100, &USER_SYS_CONFIG_File) != NULL)
+ #else
+ while (fgets(line, ASCIILINESZ, in)!=NULL)
+ #endif
+ {
+ int prev_line_len = 0;
+ int multi_line = 0;
+ int total_size = 0;
+
+ if (key)
+ {
+ free(key);
+ key = NULL;
+ }
+
+ if (val)
+ {
+ free(val);
+ val = NULL;
+ }
+
+ lineno++ ;
+ len = (int)strlen(line) - 1;
+
+ if (len == 0)
+ continue;
+
+ /* Safety check against buffer overflows */
+ #ifdef SUPPORT_FATFS
+ if (line[len] != '\n' && !f_eof(&USER_SYS_CONFIG_File))
+ #else
+ if (line[len]!='\n' && !feof(in))
+ #endif
+ {
+
+ fprintf(stderr,
+ "iniparser: input line too long in %s (%d)\n",
+ ininame,
+ lineno);
+ errs++;
+ goto out;
+ }
+
+ /* Get rid of \n and spaces at end of line */
+ while ((len >= 0) &&
+ ((line[len] == '\n') || (isspace(line[len]))))
+ {
+ line[len] = 0 ;
+ len-- ;
+ }
+
+ if (len < 0) /* Line was entirely \n and/or spaces */
+ {
+ len = 0;
+ }
+
+ /* Detect multi-line */
+ if (line[len] == '\\')
+ {
+ multi_line = 1;
+ }
+
+ if (multi_line)
+ {
+ /* Multi-line value */
+ /* length without trailing '\' */
+ /* remove multi-line indicator before appending*/
+ line[len] = 0;
+ len--;
+ }
+
+ /*
+ * If processing a multi-line then append it the previous portion,
+ * at this point 'full_line' has the previously read portion of a
+ * multi-line line (or NULL)
+ */
+ prev_line = full_line;
+ prev_line_len = 0;
+
+ if (prev_line)
+ {
+ prev_line_len = strlen(prev_line);
+ }
+
+ /* len is not strlen(line) but strlen(line) -1 */
+ total_size = (len + 1) + prev_line_len + 1;
+
+ full_line = (char *)malloc(total_size);
+
+ if (!full_line)
+ {
+ fprintf(stderr,
+ "iniparser: out of mem\n");
+ errs++;
+ goto out;
+ }
+
+ memset(full_line, 0, total_size);
+
+ if (prev_line)
+ {
+ strcpy(full_line, prev_line);
+ }
+
+ strcpy(full_line + prev_line_len, line);
+ free(prev_line);
+ prev_line = NULL;
+
+ if (multi_line)
+ {
+ continue ;
+ }
+
+ switch (iniparser_line(total_size, full_line, ¤t_section, &key, &val))
+ {
+ case LINE_EMPTY:
+ case LINE_COMMENT:
+ break ;
+
+ case LINE_SECTION:
+ if (section)
+ {
+ free(section);
+ section = NULL;
+ }
+
+ errs = dictionary_set(dict, current_section, NULL);
+ section = current_section;
+ break ;
+
+ case LINE_VALUE:
+ {
+ char *seckey;
+ /* section + ':' + key + eos */
+ seckey_size = strlen(section) + strlen(key) + 2;
+ seckey = (char *)malloc(seckey_size);
+
+ if (!seckey)
+ {
+ errs++;
+ fprintf(stderr,
+ "iniparser: out of mem\n");
+ goto out;
+ }
+
+ snprintf(seckey, seckey_size, "%s:%s", section, key);
+ errs = dictionary_set(dict, seckey, val) ;
+ free(seckey);
+ seckey = NULL;
+ }
+ break ;
+
+ case LINE_ERROR:
+ fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+ ininame,
+ lineno);
+ fprintf(stderr, "-> %s\n", full_line);
+ errs++ ;
+ break;
+
+ default:
+ break ;
+ }
+
+ memset(line, 0, ASCIILINESZ);
+
+ if (full_line)
+ {
+ free(full_line);
+ full_line = NULL;
+ }
+
+ if (errs < 0)
+ {
+ fprintf(stderr, "iniparser: memory allocation failure\n");
+ break ;
+ }
+ }
+
+out:
+
+ if (errs)
+ {
+ dictionary_del(dict);
+ dict = NULL ;
+ }
+
+ if (val)
+ {
+ free(val);
+ val = NULL;
+ }
+
+ if (key)
+ {
+ free(key);
+ key = NULL;
+ }
+
+ if (section)
+ {
+ free(section);
+ section = NULL;
+ }
+
+ if (full_line)
+ {
+ free(full_line);
+ full_line = NULL;
+ }
+
+ if (prev_line)
+ {
+ free(prev_line);
+ prev_line = NULL;
+ }
+
+ #ifdef SUPPORT_FATFS
+ f_close(&USER_SYS_CONFIG_File);
+ #else
+ if (in)
+ {
+ fclose(in);
+ in = NULL;
+ }
+ #endif
+ return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+ dictionary_del(d);
+}
+
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/components/utils/Makefile b/components/utils/Makefile
old mode 100644
new mode 100755
index 821b1c51..32d4f597
--- a/components/utils/Makefile
+++ b/components/utils/Makefile
@@ -24,10 +24,13 @@ LOCALDIR = $(patsubst %/,%,$(subst $(realpath $(QTOP))/,,$(CUR_DIR)))
####################################################################
-TREE_LIB_ENABLE=0
+TREE_LIB_ENABLE=y
lib=
subdirs=
+CFGFLAGS += -I$(QTOP)/components/utils/JSON/include
+CFGFLAGS += -I$(QTOP)/components/utils/INI/include
+
include ${QTOP}/qmk/generic/Make.tpl