summaryrefslogtreecommitdiff
path: root/apps/system/inifile
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-01-15 17:52:06 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-01-15 17:52:06 -0600
commit732a563c81c57d1fa693f4216216d0b3e01a1ee2 (patch)
tree1439189da2796fbc1d02f66b5eb7084999c49ff2 /apps/system/inifile
parentfb8316094157de2df6b613de6914dadbd8ff3476 (diff)
downloadnuttx-732a563c81c57d1fa693f4216216d0b3e01a1ee2.tar.gz
nuttx-732a563c81c57d1fa693f4216216d0b3e01a1ee2.tar.bz2
nuttx-732a563c81c57d1fa693f4216216d0b3e01a1ee2.zip
Add a tiny INI file parser
Diffstat (limited to 'apps/system/inifile')
-rw-r--r--apps/system/inifile/.gitignore11
-rw-r--r--apps/system/inifile/Kconfig28
-rw-r--r--apps/system/inifile/Makefile96
-rw-r--r--apps/system/inifile/inifile.c690
4 files changed, 825 insertions, 0 deletions
diff --git a/apps/system/inifile/.gitignore b/apps/system/inifile/.gitignore
new file mode 100644
index 000000000..83bd7b811
--- /dev/null
+++ b/apps/system/inifile/.gitignore
@@ -0,0 +1,11 @@
+/Make.dep
+/.depend
+/.built
+/*.asm
+/*.rel
+/*.lst
+/*.sym
+/*.adb
+/*.lib
+/*.src
+/*.obj
diff --git a/apps/system/inifile/Kconfig b/apps/system/inifile/Kconfig
new file mode 100644
index 000000000..012ac2b12
--- /dev/null
+++ b/apps/system/inifile/Kconfig
@@ -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
diff --git a/apps/system/inifile/Makefile b/apps/system/inifile/Makefile
new file mode 100644
index 000000000..c867c114d
--- /dev/null
+++ b/apps/system/inifile/Makefile
@@ -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
+
diff --git a/apps/system/inifile/inifile.c b/apps/system/inifile/inifile.c
new file mode 100644
index 000000000..9689d12e3
--- /dev/null
+++ b/apps/system/inifile/inifile.c
@@ -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);
+ }
+}