diff options
Diffstat (limited to 'apps/system/inifile/inifile.c')
-rw-r--r-- | apps/system/inifile/inifile.c | 1380 |
1 files changed, 690 insertions, 690 deletions
diff --git a/apps/system/inifile/inifile.c b/apps/system/inifile/inifile.c index 9689d12e3..a486eeb2b 100644 --- a/apps/system/inifile/inifile.c +++ b/apps/system/inifile/inifile.c @@ -1,690 +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);
- }
-}
+/**************************************************************************** + * 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 next line. 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 '=' delimiter. 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 function + * 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); + } +} |