* apps/fsutils/inifile/inifile.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <debug.h>
#include "fsutils/inifile.h"
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_FSUTILS_INIFILE_MAXLINE
# define CONFIG_FSUTILS_INIFILE_MAXLINE 256
#endif
#ifndef CONFIG_FSUTILS_INIFILE_DEBUGLEVEL
# define CONFIG_FSUTILS_INIFILE_DEBUGLEVEL 0
#endif
#if CONFIG_FSUTILS_INIFILE_DEBUGLEVEL < 1
# define inidbg _none
#elif defined(CONFIG_CPP_HAVE_VARARGS)
# define inidbg(format, ...) \
printf(EXTRA_FMT format EXTRA_ARG, ##__VA_ARGS__)
#else
# define inidbg printf
#endif
#if CONFIG_FSUTILS_INIFILE_DEBUGLEVEL < 2
# define iniinfo _none
#elif defined(CONFIG_CPP_HAVE_VARARGS)
# define iniinfo(format, ...) \
printf(EXTRA_FMT format EXTRA_ARG, ##__VA_ARGS__)
#else
# define iniinfo printf
#endif
* Private Types
****************************************************************************/
struct inifile_var_s
{
FAR char *variable;
FAR char *value;
};
struct inifile_state_s
{
FILE *instream;
int nextch;
char line[CONFIG_FSUTILS_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 bool 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)
{
while ((priv->nextch != '\n') && (priv->nextch != EOF))
{
priv->nextch = getc(priv->instream);
}
* 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;
* of the next line, read until the end of line indication is found (or
* until the line buffer is full). This is basically fgets().
*/
nbytes = 0;
while ((nbytes < CONFIG_FSUTILS_INIFILE_MAXLINE) &&
(priv->nextch != EOF) &&
(priv->nextch != '\n'))
{
if (priv->nextch != '\r')
{
if (nbytes || (priv->nextch != ' ' && priv->nextch != '\t'))
{
priv->line[nbytes] = priv->nextch;
nbytes++;
}
}
priv->nextch = getc(priv->instream);
}
priv->line[nbytes] = '\0';
* 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)
{
inifile_next_line(priv);
}
* 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;
* 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;
* first character from the INI file.
*/
rewind(priv->instream);
priv->nextch = getc(priv->instream);
* INI file.
*/
do
{
* bytes means nothing special here -- could be EOF or a blank line.
*/
nbytes = inifile_read_noncomment_line(priv);
* section header.
*/
if (nbytes >= 3)
{
if (priv->line[0] == '[')
{
* after the left bracket.
*/
FAR char *sectend;
sectend = strchr(&priv->line[1], ']');
* terminator.
*/
if (sectend)
{
*sectend = '\0';
}
if (strcasecmp(&priv->line[1], section) == 0)
{
return true;
}
}
}
}
while (priv->nextch != EOF);
* 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 bool inifile_read_variable(FAR struct inifile_state_s *priv,
FAR struct inifile_var_s *varinfo)
{
FAR char *ptr;
* the section is found, or (3) a valid variable assignment is found.
*/
for (; ; )
{
int nbytes = inifile_read_noncomment_line(priv);
* beginning of a new section
*/
if (!nbytes || priv->line[0] == '[')
{
return false;
}
* be NULL terminated by inifile_read_noncomment_line().
*/
ptr = strchr(&priv->line[1], '=');
if (ptr)
{
* variable value (replacing the equal sign).
*/
*ptr = '\0';
* a NULL string
*/
varinfo->variable = (FAR 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)
{
* of the section, or (3) we find the variable that we are looking
* for/
*/
iniinfo("variable=\"%s\"\n", variable);
for (; ; )
{
struct inifile_var_s varinfo;
bool found = inifile_read_variable(priv, &varinfo);
if (!found)
{
iniinfo("Returning NULL\n");
return NULL;
}
iniinfo("varinfo.variable=\"%s\"\n", varinfo.variable);
if (strcasecmp(varinfo.variable, variable) == 0)
{
iniinfo("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 FAR char *inifile_find_variable(FAR struct inifile_state_s *priv,
FAR const char *section,
FAR const char *variable)
{
FAR char *ret = NULL;
iniinfo("section=\"%s\" variable=\"%s\"\n", section, variable);
if (priv->instream && inifile_seek_to_section(priv, section))
{
* the section
*/
FAR char *value = inifile_find_section_variable(priv, variable);
iniinfo("variable_value=0x%p\n", value);
if (value && *value)
{
iniinfo("variable_value=\"%s\"\n", value);
ret = value;
}
}
iniinfo("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 const char *inifile_name)
{
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;
}
priv->instream = fopen(inifile_name, "r");
if (priv->instream)
{
priv->nextch = getc(priv->instream);
return (INIHANDLE)priv;
}
else
{
inidbg("ERROR: Could not open \"%s\"\n", inifile_name);
free(priv);
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)
{
if (priv->instream)
{
fclose(priv->instream);
}
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;
value = inifile_find_variable(priv, section, variable);
if (!value)
{
value = defvalue;
}
* We do this even if the default value is used because the caller
* will (eventually) deallocate it.
*/
if (value)
{
ret = strdup(value);
}
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;
iniinfo("section=\"%s\" variable=\"%s\" defvalue=%ld\n",
section, variable, defvalue);
value = inifile_find_variable(priv, section, variable);
if (value)
{
* ignore all conversion errors.
*/
iniinfo("%s=\"%s\"\n", variable, value);
ret = strtol(value, NULL, 0);
}
iniinfo("Returning %ld\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);
}
}