Add a tiny INI file parser
This commit is contained in:
parent
d89adb76d8
commit
d97b9dac6d
@ -776,4 +776,6 @@
|
||||
return data, and environment variables (2014-1-11).
|
||||
* apps/nshlib/nsh_parse.c: Fix a memory leak ... forgot to close
|
||||
a temporary file (2013-1-12).
|
||||
|
||||
* apps/system/inifile: A simple INI file parser (perhaps too simple).
|
||||
This is code that I wrote a long time ago and have used many time but
|
||||
is untested in its current incarnation (2014-1-15).
|
||||
|
2
Kconfig
2
Kconfig
@ -39,6 +39,6 @@ menu "Platform-specific Support"
|
||||
source "$APPSDIR/platform/Kconfig"
|
||||
endmenu
|
||||
|
||||
menu "System NSH Add-Ons"
|
||||
menu "System Libraries and NSH Add-Ons"
|
||||
source "$APPSDIR/system/Kconfig"
|
||||
endmenu
|
||||
|
138
include/inifile.h
Normal file
138
include/inifile.h
Normal file
@ -0,0 +1,138 @@
|
||||
/****************************************************************************
|
||||
* apps/include/inifile.h
|
||||
*
|
||||
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __APPS_INCLUDE_INIFILE_H
|
||||
#define __APPS_INCLUDE_INIFILE_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-Processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
typedef FAR void *INIHANDLE;
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_initialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize for access to the INI file 'inifile_name'
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
INIHANDLE inifile_initialize(FAR char *inifile_name);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_uninitialize
|
||||
*
|
||||
* Description:
|
||||
* Free resources commit to INI file parsing
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void inifile_uninitialize(INIHANDLE handle);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_read_string
|
||||
*
|
||||
* Description:
|
||||
* Obtains the specified string value for the specified variable name
|
||||
* within the specified section of the INI file. The receiver of the
|
||||
* value string should call inifile_free_string when it no longer needs
|
||||
* the memory held by the value string.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR char *inifile_read_string(INIHANDLE handle,
|
||||
FAR const char *section,
|
||||
FAR const char *variable,
|
||||
FAR const char *defvalue);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_read_integer
|
||||
*
|
||||
* Description:
|
||||
* Obtains the specified integer value for the specified variable name
|
||||
* within the specified section of the INI file
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
long inifile_read_integer(INIHANDLE handle,
|
||||
FAR const char *section,
|
||||
FAR const char *variable,
|
||||
FAR long defvalue);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_free_string
|
||||
*
|
||||
* Description:
|
||||
* Release resources allocated for the value string previously obtained
|
||||
* from inifile_read_string. The purpose of this inline function is to
|
||||
* hide the memory allocator used by this implementation.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void inifile_free_string(FAR char *value);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __APPS_INCLUDE_INIFILE_H */
|
@ -19,6 +19,10 @@ menu "I2C tool"
|
||||
source "$APPSDIR/system/i2c/Kconfig"
|
||||
endmenu
|
||||
|
||||
menu "INI File Parser"
|
||||
source "$APPSDIR/system/inifile/Kconfig"
|
||||
endmenu
|
||||
|
||||
menu "FLASH Program Installation"
|
||||
source "$APPSDIR/system/install/Kconfig"
|
||||
endmenu
|
||||
|
@ -50,6 +50,10 @@ ifeq ($(CONFIG_SYSTEM_I2CTOOL),y)
|
||||
CONFIGURED_APPS += system/i2c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SYSTEM_INIFILE),y)
|
||||
CONFIGURED_APPS += system/inifile
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SYSTEM_INSTALL),y)
|
||||
CONFIGURED_APPS += system/install
|
||||
endif
|
||||
|
@ -37,9 +37,9 @@
|
||||
|
||||
# Sub-directories containing system task
|
||||
|
||||
SUBDIRS = cdcacm composite flash_eraseall free i2c install nxplayer
|
||||
SUBDIRS += poweroff ramtest ramtron readline sdcard stackmonitor sysinfo
|
||||
SUBDIRS += usbmonitor usbmsc zmodem
|
||||
SUBDIRS = cdcacm composite flash_eraseall free i2c inifile install
|
||||
SUBDIRS += nxplayer poweroff ramtest ramtron readline sdcard stackmonitor
|
||||
SUBDIRS += sysinfo usbmonitor usbmsc zmodem
|
||||
|
||||
# Create the list of installed runtime modules (INSTALLED_DIRS)
|
||||
|
||||
|
11
system/inifile/.gitignore
vendored
Normal file
11
system/inifile/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/Make.dep
|
||||
/.depend
|
||||
/.built
|
||||
/*.asm
|
||||
/*.rel
|
||||
/*.lst
|
||||
/*.sym
|
||||
/*.adb
|
||||
/*.lib
|
||||
/*.src
|
||||
/*.obj
|
28
system/inifile/Kconfig
Normal file
28
system/inifile/Kconfig
Normal file
@ -0,0 +1,28 @@
|
||||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see misc/tools/kconfig-language.txt.
|
||||
#
|
||||
|
||||
config SYSTEM_INIFILE
|
||||
bool "INI file parser"
|
||||
default n
|
||||
---help---
|
||||
Enable support for a simple INI file parser.
|
||||
|
||||
if SYSTEM_INIFILE
|
||||
|
||||
config SYSTEM_INIFILE_MAXLINE
|
||||
int "Max line length"
|
||||
default 256
|
||||
---help---
|
||||
The largest line that the parser can expect to see in an INI file.
|
||||
|
||||
config SYSTEM_INIFILE_DEBUGLEVEL
|
||||
int "Debug level"
|
||||
default 0
|
||||
range 0 2
|
||||
---help---
|
||||
0=Debug off; 1=Print errors on console; 2=Print debug information
|
||||
on the console.
|
||||
|
||||
endif # SYSTEM_INIFILE
|
96
system/inifile/Makefile
Normal file
96
system/inifile/Makefile
Normal file
@ -0,0 +1,96 @@
|
||||
############################################################################
|
||||
# apps/system/initfile/Makefile
|
||||
#
|
||||
# Copyright (C) 2014 Gregory Nutt. All rights reserved.
|
||||
# Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name NuttX nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
-include $(TOPDIR)/.config
|
||||
-include $(TOPDIR)/Make.defs
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# I2C tool
|
||||
|
||||
ASRCS =
|
||||
CSRCS = inifile.c
|
||||
|
||||
AOBJS = $(ASRCS:.S=$(OBJEXT))
|
||||
COBJS = $(CSRCS:.c=$(OBJEXT))
|
||||
|
||||
SRCS = $(ASRCS) $(CSRCS)
|
||||
OBJS = $(AOBJS) $(COBJS)
|
||||
|
||||
ifeq ($(CONFIG_WINDOWS_NATIVE),y)
|
||||
BIN = ..\..\libapps$(LIBEXT)
|
||||
else
|
||||
ifeq ($(WINTOOL),y)
|
||||
BIN = ..\\..\\libapps$(LIBEXT)
|
||||
else
|
||||
BIN = ../../libapps$(LIBEXT)
|
||||
endif
|
||||
endif
|
||||
|
||||
ROOTDEPPATH = --dep-path .
|
||||
VPATH =
|
||||
|
||||
# Build targets
|
||||
|
||||
all: .built
|
||||
.PHONY: context .depend depend clean distclean
|
||||
|
||||
$(AOBJS): %$(OBJEXT): %.S
|
||||
$(call ASSEMBLE, $<, $@)
|
||||
|
||||
$(COBJS): %$(OBJEXT): %.c
|
||||
$(call COMPILE, $<, $@)
|
||||
|
||||
.built: $(OBJS)
|
||||
$(call ARCHIVE, $(BIN), $(OBJS))
|
||||
$(Q) touch .built
|
||||
|
||||
context:
|
||||
|
||||
.depend: Makefile $(SRCS)
|
||||
$(Q) $(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep
|
||||
$(Q) touch $@
|
||||
|
||||
depend: .depend
|
||||
|
||||
clean:
|
||||
$(call DELFILE, .built)
|
||||
$(call CLEAN)
|
||||
|
||||
distclean: clean
|
||||
$(call DELFILE, Make.dep)
|
||||
$(call DELFILE, .depend)
|
||||
|
||||
-include Make.dep
|
||||
|
690
system/inifile/inifile.c
Normal file
690
system/inifile/inifile.c
Normal file
@ -0,0 +1,690 @@
|
||||
/****************************************************************************
|
||||
* apps/system/inifile/inifile.c
|
||||
*
|
||||
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <apps/inifile.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* The maximum size of a line in the INI file */
|
||||
|
||||
#ifndef CONFIG_SYSTEM_INIFILE_MAXLINE
|
||||
# define CONFIG_SYSTEM_INIFILE_MAXLINE 256
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYSTEM_INIFILE_DEBUGLEVEL
|
||||
# define CONFIG_SYSTEM_INIFILE_DEBUGLEVEL 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPP_HAVE_VARARGS
|
||||
# if CONFIG_SYSTEM_INIFILE_DEBUGLEVEL > 0
|
||||
# define inidbg(format, arg...) \
|
||||
printf(EXTRA_FMT format EXTRA_ARG, ##arg)
|
||||
# else
|
||||
# define inidbg(x...)
|
||||
# endif
|
||||
|
||||
# if CONFIG_SYSTEM_INIFILE_DEBUGLEVEL > 1
|
||||
# define inivdbg(format, arg...) \
|
||||
printf(EXTRA_FMT format EXTRA_ARG, ##arg)
|
||||
# else
|
||||
# define inivdbg(x...)
|
||||
# endif
|
||||
#else
|
||||
# if CONFIG_SYSTEM_INIFILE_DEBUGLEVEL > 0
|
||||
# define inidbg printf
|
||||
# else
|
||||
# define inidbg (void)
|
||||
# endif
|
||||
|
||||
# if CONFIG_SYSTEM_INIFILE_DEBUGLEVEL > 1
|
||||
# define inivdbg printf
|
||||
# else
|
||||
# define inivdbg (void)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
/* A structure that describes one entry from the INI file */
|
||||
|
||||
struct inifile_var_s
|
||||
{
|
||||
FAR char *variable;
|
||||
FAR char *value;
|
||||
};
|
||||
|
||||
/* This structure describes the state of one instance of the INI file parser */
|
||||
|
||||
struct inifile_state_s
|
||||
{
|
||||
FILE *instream;
|
||||
int nextch;
|
||||
char line[CONFIG_SYSTEM_INIFILE_MAXLINE+1];
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static bool inifile_next_line(FAR struct inifile_state_s *priv);
|
||||
static int inifile_read_line(FAR struct inifile_state_s *priv);
|
||||
static int inifile_read_noncomment_line(FAR struct inifile_state_s *priv);
|
||||
static bool inifile_seek_to_section(FAR struct inifile_state_s *priv,
|
||||
FAR const char *section);
|
||||
static int inifile_read_variable(FAR struct inifile_state_s *priv,
|
||||
FAR struct inifile_var_s *varinfo);
|
||||
static FAR char *
|
||||
inifile_find_section_variable(FAR struct inifile_state_s *priv,
|
||||
FAR const char *variable);
|
||||
static FAR char *
|
||||
inifile_find_variable(FAR struct inifile_state_s *priv,
|
||||
FAR const char *section, FAR const char *variable);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_next_line
|
||||
*
|
||||
* Description:
|
||||
* Skip to the first character of the next line. Returns true if the end
|
||||
* of file was not encountered.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static bool inifile_next_line(FAR struct inifile_state_s *priv)
|
||||
{
|
||||
/* Search ahead for the end of line mark (or possibly the end of file mark) */
|
||||
|
||||
while ((priv->nextch != '\n') && (priv->nextch != EOF))
|
||||
{
|
||||
priv->nextch = getc(priv->instream);
|
||||
}
|
||||
|
||||
/* Re-prime the pump with the first character from the nextline. NOTE:
|
||||
* this logic depends on the fact that getc() will return EOF repeatedly.
|
||||
*/
|
||||
|
||||
priv->nextch = getc(priv->instream);
|
||||
return (priv->nextch != EOF);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_read_line
|
||||
*
|
||||
* Description:
|
||||
* Read the next line from the INI file into the line buffer and return
|
||||
* the number of characters read into the buffer. If we hit the end of a
|
||||
* section (or the end of a file), this function will return a count of
|
||||
* zero.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int inifile_read_line(FAR struct inifile_state_s *priv)
|
||||
{
|
||||
int nbytes;
|
||||
|
||||
/* Assuming that the file pointer is correctly positioned at the beginning
|
||||
* of the next line, read until the end of line indication is found (or
|
||||
* until the line buffer is full).
|
||||
*/
|
||||
|
||||
nbytes = 0;
|
||||
while ((nbytes < CONFIG_SYSTEM_INIFILE_MAXLINE) &&
|
||||
(priv->nextch != EOF) &&
|
||||
(priv->nextch != '\n'))
|
||||
{
|
||||
/* Always ignore carriage returns */
|
||||
|
||||
if (priv->nextch != '\r')
|
||||
{
|
||||
/* Ignore any leading whitespace on the line */
|
||||
|
||||
if (nbytes || (priv->nextch != ' ' && priv->nextch != '\t'))
|
||||
{
|
||||
/* Add the new character to the line buffer */
|
||||
|
||||
priv->line[nbytes] = priv->nextch;
|
||||
nbytes++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the next character from the INI file */
|
||||
|
||||
priv->nextch = getc(priv->instream);
|
||||
}
|
||||
|
||||
/* NUL terminate the string */
|
||||
|
||||
priv->line[nbytes] = '\0';
|
||||
|
||||
/* Skip to the first character of the next line. This should normally
|
||||
* just amount to skipping over the newline, but could be more involved
|
||||
* if we had to truncate the line to fit into the line buffer.
|
||||
*/
|
||||
|
||||
if (priv->nextch != EOF)
|
||||
{
|
||||
(void)inifile_next_line(priv);
|
||||
}
|
||||
|
||||
/* And return the number of bytes read (excluding the NUL terminator and
|
||||
* leading whitespace).
|
||||
*/
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_read_noncomment_line
|
||||
*
|
||||
* Description:
|
||||
* Read until either a (1) no further lines are found in the file, or (2)
|
||||
* a line that does not begin with a semi-colon is found
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int inifile_read_noncomment_line(FAR struct inifile_state_s *priv)
|
||||
{
|
||||
int nbytes;
|
||||
|
||||
/* Read until either a (1) no further lines are found in
|
||||
* the file, or (2) a line that does not begin with a semi-colon
|
||||
* is found */
|
||||
|
||||
do nbytes = inifile_read_line(priv);
|
||||
while (nbytes > 0 && priv->line[0] == ';');
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_seek_to_section
|
||||
*
|
||||
* Description:
|
||||
* Positions the file pointer to the line containing the first variable
|
||||
* description within the INI file. Returns 1 if the section was found.
|
||||
* In this case, the file pointer will be positioned at the beginning of
|
||||
* the first variable line.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static bool inifile_seek_to_section(FAR struct inifile_state_s *priv,
|
||||
FAR const char *section)
|
||||
{
|
||||
int nbytes;
|
||||
|
||||
/* Rewind to the beginning of the INI file and re-prime the pump with the
|
||||
* first character from the INI file.
|
||||
*/
|
||||
|
||||
rewind(priv->instream);
|
||||
priv->nextch = getc(priv->instream);
|
||||
|
||||
/* Loop until either the section is found, or until we hit the end of the
|
||||
* INI file.
|
||||
*/
|
||||
|
||||
do
|
||||
{
|
||||
/* Read the next line into the input buffer. A returned value of zero
|
||||
* bytes means nothing special here -- could be EOF or a blank line.
|
||||
*/
|
||||
|
||||
nbytes = inifile_read_noncomment_line(priv);
|
||||
|
||||
/* It takes at least three bytes of data to be a candidate for a
|
||||
* section header.
|
||||
*/
|
||||
|
||||
if (nbytes >= 3)
|
||||
{
|
||||
/* A section header must begin with a left bracket */
|
||||
|
||||
if (priv->line[0] == '[')
|
||||
{
|
||||
/* The section name should start with the first character
|
||||
* after the left bracket.
|
||||
*/
|
||||
|
||||
FAR char *sectend = &priv->line[1];
|
||||
|
||||
/* The section name should extend to the right bracket. While
|
||||
* we are looking for the end of the section name, we'll also
|
||||
* perform a conversion to lower case.
|
||||
*/
|
||||
|
||||
while (*sectend != ']' && *sectend != '\0')
|
||||
{
|
||||
/* Perform the conversion to lower case, if appropriate */
|
||||
|
||||
int ch = (int)*sectend;
|
||||
if ((ch >= 'A') && ( ch <= 'Z'))
|
||||
{
|
||||
*sectend = (char)(ch - 'A' + 'a');
|
||||
}
|
||||
|
||||
/* Skip to the next character */
|
||||
|
||||
sectend++;
|
||||
}
|
||||
|
||||
/* Add NULL termination (This is unnecessary in the case where
|
||||
* the line was truncated
|
||||
*/
|
||||
|
||||
*sectend = '\0';
|
||||
|
||||
/* Then compare the section name to the one we are looking for */
|
||||
|
||||
if (strcmp(&priv->line[1], section) == 0)
|
||||
{
|
||||
/* The section names match! Return success */
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (priv->nextch != EOF);
|
||||
|
||||
/* If we got here, we search the whole INI file without finding
|
||||
* the requested section
|
||||
*/
|
||||
|
||||
inidbg("ERROR: Section \"%s\" not found\n", section);
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_read_variable
|
||||
*
|
||||
* Description:
|
||||
* Obtain variable info from the next line in the section. This assumes
|
||||
* that the file pointer is pointing to the beginning of the next line.
|
||||
* If there is no further data in the section, false is returned.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int inifile_read_variable(FAR struct inifile_state_s *priv,
|
||||
FAR struct inifile_var_s *varinfo)
|
||||
{
|
||||
FAR char *ptr;
|
||||
|
||||
/* Read until either (1) the end of file is found, (2) the end of
|
||||
* the section is found, or (3) a valid variable assignment is found.
|
||||
*/
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Read the next line in the buffer */
|
||||
|
||||
int nbytes = inifile_read_noncomment_line(priv);
|
||||
|
||||
/* Make sure that the line is non-NULL and that this is not the
|
||||
* beginning of a new section
|
||||
*/
|
||||
|
||||
if (!nbytes || priv->line[0] == '[')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Search for the '=' delimitor. NOTE the line is guaranteed to
|
||||
* be NULL terminated by inifile_read_noncomment_line().
|
||||
*/
|
||||
|
||||
for (ptr = (char*)priv->line; *ptr && *ptr != '='; ptr++)
|
||||
{
|
||||
/* Force the variable name to lower case */
|
||||
|
||||
int ch = *ptr;
|
||||
if ((ch >= 'A') && (ch <= 'Z'))
|
||||
{
|
||||
*ptr = (char)(ch - 'A' + 'a');
|
||||
}
|
||||
}
|
||||
|
||||
/* If the delimiter was found, return success */
|
||||
|
||||
if (*ptr == '=')
|
||||
{
|
||||
/* Put NUL termination between the variable name and the
|
||||
* variable value (replacing the equal sign).
|
||||
*/
|
||||
|
||||
*ptr = '\0';
|
||||
|
||||
/* Set up the return structure. NOTE: value may point at
|
||||
* a NULL string
|
||||
*/
|
||||
|
||||
varinfo->variable = (char*)priv->line;
|
||||
varinfo->value = (ptr + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_find_section_variable
|
||||
*
|
||||
* Description:
|
||||
* Find the value string associated with the variable name. This furnction
|
||||
* will return NULL on failure to find the variable. It will return a
|
||||
* pointer to an empty string is the variable is found, but not assigned a
|
||||
* value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static FAR char *
|
||||
inifile_find_section_variable(FAR struct inifile_state_s *priv,
|
||||
FAR const char *variable)
|
||||
{
|
||||
/* Loop until either (1) we hit the end of file, (2) we hit the end
|
||||
* of the section, or (3) we find the variable that we are looking
|
||||
* for/
|
||||
*/
|
||||
|
||||
inivdbg("variable=\"%s\"\n", variable);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Get the next variable from this section. */
|
||||
|
||||
struct inifile_var_s varinfo;
|
||||
bool found = inifile_read_variable(priv, &varinfo);
|
||||
|
||||
/* Is there anything left in the section? */
|
||||
|
||||
if (!found)
|
||||
{
|
||||
inivdbg("Returning NULL\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inivdbg("varinfo.variable=\"%s\"\n", varinfo.variable);
|
||||
|
||||
/* Does the the variable name match the one we are looking for? */
|
||||
|
||||
if (strcmp(varinfo.variable, variable) == 0)
|
||||
{
|
||||
/* Yes... then we got it! */
|
||||
|
||||
inivdbg("Returning \"%s\"\n", varinfo.value);
|
||||
return varinfo.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_find_variable
|
||||
*
|
||||
* Description:
|
||||
* Obtains the specified string value for the specified variable name
|
||||
* within the specified section of the INI file.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static char *inifile_find_variable(FAR struct inifile_state_s *priv,
|
||||
FAR const char *section,
|
||||
FAR const char *variable)
|
||||
{
|
||||
FAR char *ret = NULL;
|
||||
|
||||
inivdbg("section=\"%s\" variable=\"%s\"\n", section, variable);
|
||||
|
||||
/* Seek to the first variable in the specified section of the INI file */
|
||||
|
||||
if (priv->instream && inifile_seek_to_section(priv, section))
|
||||
{
|
||||
/* If the seek was successful, then find the value string within
|
||||
* the section
|
||||
*/
|
||||
|
||||
FAR char *value = inifile_find_section_variable(priv, variable);
|
||||
inivdbg("ariable_value=0x%p\n", value);
|
||||
|
||||
if (value && *value)
|
||||
{
|
||||
inivdbg("variable_value=\"%s\"\n", value);
|
||||
ret = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the string that we found. */
|
||||
|
||||
inivdbg("Returning 0x%p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_initialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize for access to the INI file 'inifile_name'
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
INIHANDLE inifile_initialize(FAR char *inifile_name)
|
||||
{
|
||||
/* Allocate an INI file parser state structure */
|
||||
|
||||
FAR struct inifile_state_s *priv =
|
||||
(FAR struct inifile_state_s *)malloc(sizeof(struct inifile_state_s));
|
||||
|
||||
if (!priv)
|
||||
{
|
||||
inidbg("ERROR: Failed to allocate state structure\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Open the specified INI file for reading */
|
||||
|
||||
priv->instream = fopen(inifile_name, "r");
|
||||
|
||||
/* Prime the pump */
|
||||
|
||||
if (priv->instream)
|
||||
{
|
||||
priv->nextch = getc(priv->instream);
|
||||
return (INIHANDLE)priv;
|
||||
}
|
||||
else
|
||||
{
|
||||
inidbg("ERROR: Could not open \"%s\"\n", inifile_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_uninitialize
|
||||
*
|
||||
* Description:
|
||||
* Free resources commit to INI file parsing
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void inifile_uninitialize(INIHANDLE handle)
|
||||
{
|
||||
FAR struct inifile_state_s *priv = (FAR struct inifile_state_s *)handle;
|
||||
|
||||
if (priv)
|
||||
{
|
||||
/* Close the INI file stream */
|
||||
|
||||
if (priv->instream)
|
||||
{
|
||||
fclose(priv->instream);
|
||||
}
|
||||
|
||||
/* Release the state structure */
|
||||
|
||||
free(priv);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_read_string
|
||||
*
|
||||
* Description:
|
||||
* Obtains the specified string value for the specified variable name
|
||||
* within the specified section of the INI file. The receiver of the
|
||||
* value string should call inifile_free_string when it no longer needs
|
||||
* the memory held by the value string.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR char *inifile_read_string(INIHANDLE handle,
|
||||
FAR const char *section,
|
||||
FAR const char *variable,
|
||||
FAR const char *defvalue)
|
||||
{
|
||||
FAR struct inifile_state_s *priv = (FAR struct inifile_state_s *)handle;
|
||||
FAR char *ret = NULL;
|
||||
FAR const char *value;
|
||||
|
||||
/* Get a reference to the volatile version of the string */
|
||||
|
||||
value = inifile_find_variable(priv, section, variable);
|
||||
|
||||
/* If the variable was not found, then use the default value */
|
||||
|
||||
if (!value)
|
||||
{
|
||||
/* Selecting the default string */
|
||||
|
||||
value = defvalue;
|
||||
}
|
||||
|
||||
/* If this was successful, create a non-volatile copy of the string
|
||||
* We do this even if the default value is used because the caller
|
||||
* will (eventually) deallocate it.
|
||||
*/
|
||||
|
||||
if (value)
|
||||
{
|
||||
ret = strdup(value);
|
||||
}
|
||||
|
||||
/* Return the string that we found. */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_read_integer
|
||||
*
|
||||
* Description:
|
||||
* Obtains the specified integer value for the specified variable name
|
||||
* within the specified section of the INI file
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
long inifile_read_integer(INIHANDLE handle,
|
||||
FAR const char *section,
|
||||
FAR const char *variable,
|
||||
FAR long defvalue)
|
||||
{
|
||||
FAR struct inifile_state_s *priv = (FAR struct inifile_state_s *)handle;
|
||||
FAR char *value;
|
||||
long ret = defvalue;
|
||||
|
||||
/* Assume failure to find the requested value */
|
||||
|
||||
inivdbg("section=\"%s\" variable=\"%s\" defvalue=%d\n",
|
||||
section, variable, defvalue);
|
||||
|
||||
/* Get the value as a string first */
|
||||
|
||||
value = inifile_find_variable(priv, section, variable);
|
||||
|
||||
/* If this was successful, then convert the string to an integer value. */
|
||||
|
||||
if (value)
|
||||
{
|
||||
/* Then convert the string to an integer value */
|
||||
|
||||
inivdbg("%s=\"%s\"\n", variable, value);
|
||||
ret = strtol(value, NULL, 10);
|
||||
}
|
||||
|
||||
/* Return the value that we found. */
|
||||
|
||||
inivdbg("Returning %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inifile_free_string
|
||||
*
|
||||
* Description:
|
||||
* Release resources allocated for the value string previously obtained
|
||||
* from inifile_read_string. The purpose of this inline function is to
|
||||
* hide the memory allocator used by this implementation.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void inifile_free_string(FAR char *value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
free(value);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user