mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
527 lines
16 KiB
C
527 lines
16 KiB
C
/*
|
|
* This file is part of the OpenMV project.
|
|
*
|
|
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
|
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
|
*
|
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
|
*
|
|
* Initialization file parser.
|
|
*/
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "ini.h"
|
|
#include "file_utils.h"
|
|
|
|
/*-------------------------------------------------------------------------
|
|
_isppace.c - part of ctype.h
|
|
|
|
Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999)
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by the
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
In other words, you are welcome to use, share and improve this program.
|
|
You are forbidden to forbid anyone else to use, share and improve
|
|
what you give them. Help stamp out software-hoarding!
|
|
-------------------------------------------------------------------------*/
|
|
|
|
char ini_isspace(unsigned char c) {
|
|
if (c == ' '
|
|
|| c == '\f'
|
|
|| c == '\n'
|
|
|| c == '\r'
|
|
|| c == '\t'
|
|
|| c == '\v') {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* atoi.c --
|
|
*
|
|
* Source code for the "atoi" library procedure.
|
|
*
|
|
* Copyright 1988 Regents of the University of California
|
|
* Permission to use, copy, modify, and distribute this
|
|
* software and its documentation for any purpose and without
|
|
* fee is hereby granted, provided that the above copyright
|
|
* notice appear in all copies. The University of California
|
|
* makes no representations about the suitability of this
|
|
* software for any purpose. It is provided "as is" without
|
|
* express or implied warranty.
|
|
*/
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* atoi --
|
|
*
|
|
* Convert an ASCII string into an integer.
|
|
*
|
|
* Results:
|
|
* The return value is the integer equivalent of string. If there
|
|
* are no decimal digits in string, then 0 is returned.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
ini_atoi(string)
|
|
const char *string; /* String of ASCII digits, possibly
|
|
* preceded by white space. For bases
|
|
* greater than 10, either lower- or
|
|
* upper-case digits may be used.
|
|
*/
|
|
{
|
|
register int result = 0;
|
|
register unsigned int digit;
|
|
int sign;
|
|
|
|
/*
|
|
* Skip any leading blanks.
|
|
*/
|
|
|
|
while (ini_isspace(*string)) {
|
|
string += 1;
|
|
}
|
|
|
|
/*
|
|
* Check for a sign.
|
|
*/
|
|
|
|
if (*string == '-') {
|
|
sign = 1;
|
|
string += 1;
|
|
} else {
|
|
sign = 0;
|
|
if (*string == '+') {
|
|
string += 1;
|
|
}
|
|
}
|
|
|
|
for (; *string; string++) {
|
|
digit = *string - '0';
|
|
if ((digit < 0) || (digit > 9)) {
|
|
break;
|
|
}
|
|
result = (10 * result) + digit;
|
|
}
|
|
|
|
if (sign) {
|
|
return -result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool ini_is_true(const char *value) {
|
|
int i = ini_atoi(value);
|
|
if (i) {
|
|
return true;
|
|
}
|
|
if (strlen(value) != 4) {
|
|
return false;
|
|
}
|
|
if ((value[0] != 'T') && (value[0] != 't')) {
|
|
return false;
|
|
}
|
|
if ((value[1] != 'R') && (value[1] != 'r')) {
|
|
return false;
|
|
}
|
|
if ((value[2] != 'U') && (value[2] != 'u')) {
|
|
return false;
|
|
}
|
|
if ((value[3] != 'E') && (value[3] != 'e')) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
|
|
*
|
|
* 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Opsycon AB.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*
|
|
*/
|
|
|
|
/** ini_fgetc(fp) -- get char from stream */
|
|
|
|
int ini_fgetc(FIL *fp) {
|
|
char c;
|
|
UINT b;
|
|
|
|
if (file_ll_read(fp, &c, 1, &b) != FR_OK || b != 1) {
|
|
return (EOF);
|
|
}
|
|
return (c);
|
|
}
|
|
|
|
/*
|
|
* Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
|
|
*
|
|
* 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Opsycon AB.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*
|
|
*/
|
|
|
|
/** char *ini_fgets(dst,max,fp) -- get string from stream */
|
|
|
|
char *ini_fgets(char *dst, int max, FIL *fp) {
|
|
char *p;
|
|
int c = EOF;
|
|
|
|
/* get max bytes or upto a newline */
|
|
|
|
for (p = dst, max--; max > 0; max--) {
|
|
if ((c = ini_fgetc(fp)) == EOF) {
|
|
break;
|
|
}
|
|
*p++ = c;
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
*p = 0;
|
|
if (p == dst || c == EOF) {
|
|
return NULL;
|
|
}
|
|
return (p);
|
|
}
|
|
|
|
/* inih -- simple .INI file parser
|
|
|
|
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
|
home page for more info:
|
|
|
|
https://github.com/benhoyt/inih
|
|
|
|
*/
|
|
|
|
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
#if !INI_USE_STACK
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#define MAX_SECTION 50
|
|
#define MAX_NAME 50
|
|
|
|
/* Used by ini_parse_string() to keep track of string parsing state. */
|
|
typedef struct {
|
|
const char *ptr;
|
|
size_t num_left;
|
|
} ini_parse_string_ctx;
|
|
|
|
/* Strip whitespace chars off end of given string, in place. Return s. */
|
|
static char *rstrip(char *s) {
|
|
char *p = s + strlen(s);
|
|
while (p > s && ini_isspace((unsigned char) (*--p))) {
|
|
*p = '\0';
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* Return pointer to first non-whitespace char in given string. */
|
|
static char *lskip(const char *s) {
|
|
while (*s && ini_isspace((unsigned char) (*s))) {
|
|
s++;
|
|
}
|
|
return (char *) s;
|
|
}
|
|
|
|
/* Return pointer to first char (of chars) or inline comment in given string,
|
|
or pointer to null at end of string if neither found. Inline comment must
|
|
be prefixed by a whitespace character to register as a comment. */
|
|
static char *find_chars_or_comment(const char *s, const char *chars) {
|
|
#if INI_ALLOW_INLINE_COMMENTS
|
|
int was_space = 0;
|
|
while (*s && (!chars || !strchr(chars, *s)) &&
|
|
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
|
was_space = ini_isspace((unsigned char) (*s));
|
|
s++;
|
|
}
|
|
#else
|
|
while (*s && (!chars || !strchr(chars, *s))) {
|
|
s++;
|
|
}
|
|
#endif
|
|
return (char *) s;
|
|
}
|
|
|
|
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
|
static char *strncpy0(char *dest, const char *src, size_t size) {
|
|
strncpy(dest, src, size - 1);
|
|
dest[size - 1] = '\0';
|
|
return dest;
|
|
}
|
|
|
|
/* See documentation in header file. */
|
|
int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler,
|
|
void *user) {
|
|
/* Uses a fair bit of stack (use heap instead if you need to) */
|
|
#if INI_USE_STACK
|
|
char line[INI_MAX_LINE];
|
|
int max_line = INI_MAX_LINE;
|
|
#else
|
|
char *line;
|
|
int max_line = INI_INITIAL_ALLOC;
|
|
#endif
|
|
#if INI_ALLOW_REALLOC
|
|
char *new_line;
|
|
int offset;
|
|
#endif
|
|
char section[MAX_SECTION] = "";
|
|
char prev_name[MAX_NAME] = "";
|
|
|
|
char *start;
|
|
char *end;
|
|
char *name;
|
|
char *value;
|
|
int lineno = 0;
|
|
int error = 0;
|
|
|
|
#if !INI_USE_STACK
|
|
line = (char *) malloc(INI_INITIAL_ALLOC);
|
|
if (!line) {
|
|
return -2;
|
|
}
|
|
#endif
|
|
|
|
#if INI_HANDLER_LINENO
|
|
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
|
#else
|
|
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
|
#endif
|
|
|
|
/* Scan through stream line by line */
|
|
while (reader(line, max_line, stream) != NULL) {
|
|
#if INI_ALLOW_REALLOC
|
|
offset = strlen(line);
|
|
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
|
max_line *= 2;
|
|
if (max_line > INI_MAX_LINE) {
|
|
max_line = INI_MAX_LINE;
|
|
}
|
|
new_line = realloc(line, max_line);
|
|
if (!new_line) {
|
|
free(line);
|
|
return -2;
|
|
}
|
|
line = new_line;
|
|
if (reader(line + offset, max_line - offset, stream) == NULL) {
|
|
break;
|
|
}
|
|
if (max_line >= INI_MAX_LINE) {
|
|
break;
|
|
}
|
|
offset += strlen(line + offset);
|
|
}
|
|
#endif
|
|
|
|
lineno++;
|
|
|
|
start = line;
|
|
#if INI_ALLOW_BOM
|
|
if (lineno == 1 && (unsigned char) start[0] == 0xEF &&
|
|
(unsigned char) start[1] == 0xBB &&
|
|
(unsigned char) start[2] == 0xBF) {
|
|
start += 3;
|
|
}
|
|
#endif
|
|
start = lskip(rstrip(start));
|
|
|
|
if (*start == ';' || *start == '#') {
|
|
/* Per Python configparser, allow both ; and # comments at the
|
|
start of a line */
|
|
}
|
|
#if INI_ALLOW_MULTILINE
|
|
else if (*prev_name && *start && start > line) {
|
|
/* Non-blank line with leading whitespace, treat as continuation
|
|
of previous name's value (as per Python configparser). */
|
|
if (!HANDLER(user, section, prev_name, start) && !error) {
|
|
error = lineno;
|
|
}
|
|
}
|
|
#endif
|
|
else if (*start == '[') {
|
|
/* A "[section]" line */
|
|
end = find_chars_or_comment(start + 1, "]");
|
|
if (*end == ']') {
|
|
*end = '\0';
|
|
strncpy0(section, start + 1, sizeof(section));
|
|
*prev_name = '\0';
|
|
} else if (!error) {
|
|
/* No ']' found on section line */
|
|
error = lineno;
|
|
}
|
|
} else if (*start) {
|
|
/* Not a comment, must be a name[=:]value pair */
|
|
end = find_chars_or_comment(start, "=:");
|
|
if (*end == '=' || *end == ':') {
|
|
*end = '\0';
|
|
name = rstrip(start);
|
|
value = end + 1;
|
|
#if INI_ALLOW_INLINE_COMMENTS
|
|
end = find_chars_or_comment(value, NULL);
|
|
if (*end) {
|
|
*end = '\0';
|
|
}
|
|
#endif
|
|
value = lskip(value);
|
|
rstrip(value);
|
|
|
|
/* Valid name[=:]value pair found, call handler */
|
|
strncpy0(prev_name, name, sizeof(prev_name));
|
|
if (!HANDLER(user, section, name, value) && !error) {
|
|
error = lineno;
|
|
}
|
|
} else if (!error) {
|
|
/* No '=' or ':' found on name[=:]value line */
|
|
error = lineno;
|
|
}
|
|
}
|
|
|
|
#if INI_STOP_ON_FIRST_ERROR
|
|
if (error) {
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !INI_USE_STACK
|
|
free(line);
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
/* See documentation in header file. */
|
|
int ini_parse_file(FIL *file, ini_handler handler, void *user) {
|
|
return ini_parse_stream((ini_reader) ini_fgets, file, handler, user);
|
|
}
|
|
|
|
/* See documentation in header file. */
|
|
int ini_parse(FATFS *fs, const char *filename, ini_handler handler, void *user) {
|
|
FIL file;
|
|
int error;
|
|
|
|
FRESULT res = f_open(fs, &file, filename, FA_READ | FA_OPEN_EXISTING);
|
|
if (res != FR_OK) {
|
|
return -1;
|
|
}
|
|
error = ini_parse_file(&file, handler, user);
|
|
res = f_close(&file);
|
|
if (res != FR_OK) {
|
|
return -1;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/* An ini_reader function to read the next line from a string buffer. This
|
|
is the ini_fgets() equivalent used by ini_parse_string(). */
|
|
static char *ini_reader_string(char *str, int num, void *stream) {
|
|
ini_parse_string_ctx *ctx = (ini_parse_string_ctx *) stream;
|
|
const char *ctx_ptr = ctx->ptr;
|
|
size_t ctx_num_left = ctx->num_left;
|
|
char *strp = str;
|
|
char c;
|
|
|
|
if (ctx_num_left == 0 || num < 2) {
|
|
return NULL;
|
|
}
|
|
|
|
while (num > 1 && ctx_num_left != 0) {
|
|
c = *ctx_ptr++;
|
|
ctx_num_left--;
|
|
*strp++ = c;
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
num--;
|
|
}
|
|
|
|
*strp = '\0';
|
|
ctx->ptr = ctx_ptr;
|
|
ctx->num_left = ctx_num_left;
|
|
return str;
|
|
}
|
|
|
|
/* See documentation in header file. */
|
|
int ini_parse_string(const char *string, ini_handler handler, void *user) {
|
|
ini_parse_string_ctx ctx;
|
|
|
|
ctx.ptr = string;
|
|
ctx.num_left = strlen(string);
|
|
return ini_parse_stream((ini_reader) ini_reader_string, &ctx, handler,
|
|
user);
|
|
}
|