1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-12-02 10:24:11 +01:00
LibrePilot/flight/PiOS/STM32F10x/Libraries/minIni/minIni.c
gussy df6b2e4ddc Moved STM32 specific PiOS Libraries.
git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@240 ebee16cc-31ac-478f-84a7-5cbb03baadba
2010-03-04 06:14:14 +00:00

650 lines
21 KiB
C

/* minIni - Multi-Platform INI file parser, suitable for embedded systems
*
* These routines are in part based on the article "Multiplatform .INI Files"
* by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal.
*
* Copyright (c) ITB CompuPhase, 2008-2009
*
* Licensed 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.
*
* Version: $Id: minIni.c 24 2009-05-06 08:01:53Z thiadmer.riemersma $
*/
#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined MININI_ANSI
# if !defined UNICODE /* for Windows */
# define UNICODE
# endif
# if !defined _UNICODE /* for C library */
# define _UNICODE
# endif
#endif
#include "minIni.h"
#if defined NDEBUG
#define assert(e)
#else
#include <assert.h>
#endif
#if !defined __T
#include <string.h>
#include <stdlib.h>
/* definition of TCHAR already in minIni.h */
#define __T(s) s
#define _tcscat strcat
#define _tcschr strchr
#define _tcscmp strcmp
#define _tcscpy strcpy
#define _tcsicmp stricmp
#define _tcslen strlen
#define _tcsncpy strncpy
#define _tcsnicmp strnicmp
#define _tcsrchr strrchr
#define _tcstol strtol
#define _tfgets fgets
#define _tfputs fputs
#define _tfopen fopen
#define _tremove remove
#define _trename rename
#endif
#if defined __linux || defined __linux__
#define __LINUX__
#endif
#if defined FREEBSD && !defined __FreeBSD__
#define __FreeBSD__
#endif
#if !defined strnicmp
#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
#define strnicmp strncasecmp
#endif
#endif
#if !defined INI_LINETERM
#define INI_LINETERM __T("\n")
#endif
#if !defined INI_FILETYPE
#define INI_FILETYPE FILE*
#endif
#if !defined sizearray
#define sizearray(a) (sizeof(a) / sizeof((a)[0]))
#endif
enum quote_option {
QUOTE_NONE,
QUOTE_ENQUOTE,
QUOTE_DEQUOTE,
};
static TCHAR *skipleading(const TCHAR *str)
{
assert(str != NULL);
while (*str != '\0' && *str <= ' ')
str++;
return (TCHAR *)str;
}
static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base)
{
assert(str != NULL);
assert(base != NULL);
while (str > base && *(str-1) <= ' ')
str--;
return (TCHAR *)str;
}
static TCHAR *striptrailing(TCHAR *str)
{
TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str);
assert(ptr != NULL);
*ptr='\0';
return str;
}
static TCHAR *save_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option)
{
int d, s;
assert(maxlen>0);
if (option == QUOTE_ENQUOTE && maxlen < 3)
option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */
switch (option) {
case QUOTE_NONE:
_tcsncpy(dest,source,maxlen);
dest[maxlen-1]='\0';
break;
case QUOTE_ENQUOTE:
d = 0;
dest[d++] = '"';
for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) {
if (source[s] == '"') {
if (d >= maxlen - 3)
break; /* no space to store the escape character plus the one that follows it */
dest[d++] = '\\';
} /* if */
dest[d] = source[s];
} /* for */
dest[d++] = '"';
dest[d] = '\0';
break;
case QUOTE_DEQUOTE:
for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) {
if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"')
s++;
dest[d] = source[s];
} /* for */
dest[d] = '\0';
break;
default:
assert(0);
} /* switch */
return dest;
}
static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key,
int idxSection, int idxKey, TCHAR *Buffer, int BufferSize)
{
TCHAR *sp, *ep;
int len, idx, isstring;
TCHAR LocalBuffer[INI_BUFFERSIZE];
assert(fp != NULL);
/* Move through file 1 line at a time until a section is matched or EOF. If
* parameter Section is NULL, only look at keys above the first section. If
* idxSection is postive, copy the relevant section name.
*/
len = (Section != NULL) ? _tcslen(Section) : 0;
if (len > 0 || idxSection >= 0) {
idx = -1;
do {
if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp))
return 0;
sp = skipleading(LocalBuffer);
ep = _tcschr(sp, ']');
} while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection));
if (idxSection >= 0) {
if (idx == idxSection) {
assert(ep != NULL);
assert(*ep == ']');
*ep = '\0';
save_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE);
return 1;
} /* if */
return 0; /* no more section found */
} /* if */
} /* if */
/* Now that the section has been found, find the entry.
* Stop searching upon leaving the section's area.
*/
assert(Key != NULL || idxKey >= 0);
len = (Key != NULL) ? (int)_tcslen(Key) : 0;
idx = -1;
do {
if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[')
return 0;
sp = skipleading(LocalBuffer);
ep = _tcschr(sp, '='); /* Parse out the equal sign */
if (ep == NULL)
ep = _tcschr(sp, ':');
} while (*sp == ';' || *sp == '#' || ep == NULL || (((int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey));
if (idxKey >= 0) {
if (idx == idxKey) {
assert(ep != NULL);
assert(*ep == '=' || *ep == ':');
*ep = '\0';
striptrailing(sp);
save_strncpy(Buffer, sp, BufferSize, QUOTE_NONE);
return 1;
} /* if */
return 0; /* no more key found (in this section) */
} /* if */
/* Copy up to BufferSize chars to buffer */
assert(ep != NULL);
assert(*ep == '=' || *ep == ':');
sp = skipleading(ep + 1);
/* Remove a trailing comment */
isstring = 0;
for (ep = sp; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) {
if (*ep == '"') {
if (*(ep + 1) == '"')
ep++; /* skip "" (both quotes) */
else
isstring = !isstring; /* single quote, toggle isstring */
} else if (*ep == '\\' && *(ep + 1) == '"') {
ep++; /* skip \" (both quotes */
} /* if */
} /* for */
assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#'));
*ep = '\0'; /* terminate at a comment */
striptrailing(sp);
/* Remove double quotes surrounding a value */
isstring = QUOTE_NONE;
if (*sp == '"' && (ep = _tcschr(sp, '\0')) != NULL && *(ep - 1) == '"') {
sp++;
*--ep = '\0';
isstring = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */
} /* if */
save_strncpy(Buffer, sp, BufferSize, isstring);
return 1;
}
/** ini_gets()
* \param Section the name of the section to search for
* \param Key the name of the entry to find the value of
* \param DefValue default string in the event of a failed read
* \param Buffer a pointer to the buffer to copy into
* \param BufferSize the maximum number of characters to copy
* \param Filename the name and full path of the .ini file to read from
*
* \return the number of characters copied into the supplied buffer
*/
int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue,
TCHAR *Buffer, int BufferSize, const TCHAR *Filename)
{
INI_FILETYPE fp;
int ok = 0;
if (Buffer == NULL || BufferSize <= 0 || Key == NULL)
return 0;
if (ini_openread(Filename, &fp)) {
ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize);
ini_close(&fp);
} /* if */
if (!ok)
save_strncpy(Buffer, DefValue, BufferSize, QUOTE_NONE);
return _tcslen(Buffer);
}
/** ini_getl()
* \param Section the name of the section to search for
* \param Key the name of the entry to find the value of
* \param DefValue the default value in the event of a failed read
* \param Filename the name of the .ini file to read from
*
* \return the value located at Key
*/
long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename)
{
TCHAR buff[64];
int len = ini_gets(Section, Key, __T(""), buff, sizearray(buff), Filename);
return (len == 0) ? DefValue : _tcstol(buff,NULL,10);
}
/** ini_getsection()
* \param idx the zero-based sequence number of the section to return
* \param Buffer a pointer to the buffer to copy into
* \param BufferSize the maximum number of characters to copy
* \param Filename the name and full path of the .ini file to read from
*
* \return the number of characters copied into the supplied buffer
*/
int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename)
{
INI_FILETYPE fp;
int ok = 0;
if (Buffer == NULL || BufferSize <= 0 || idx < 0)
return 0;
if (ini_openread(Filename, &fp)) {
ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize);
ini_close(&fp);
} /* if */
if (!ok)
*Buffer = '\0';
return _tcslen(Buffer);
}
/** ini_getkey()
* \param Section the name of the section to browse through, or NULL to
* browse through the keys outside any section
* \param idx the zero-based sequence number of the key to return
* \param Buffer a pointer to the buffer to copy into
* \param BufferSize the maximum number of characters to copy
* \param Filename the name and full path of the .ini file to read from
*
* \return the number of characters copied into the supplied buffer
*/
int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename)
{
INI_FILETYPE fp;
int ok = 0;
if (Buffer == NULL || BufferSize <= 0 || idx < 0)
return 0;
if (ini_openread(Filename, &fp)) {
ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize);
ini_close(&fp);
} /* if */
if (!ok)
*Buffer = '\0';
return _tcslen(Buffer);
}
#if ! defined INI_READONLY
static void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength)
{
TCHAR *p;
save_strncpy(dest, source, maxlength, QUOTE_NONE);
p = _tcsrchr(dest, '\0');
assert(p != NULL);
*(p - 1) = '~';
}
static enum quote_option check_enquote(const TCHAR *Value)
{
const TCHAR *p;
/* run through the value, if it has trailing spaces, or '"', ';' or '#'
* characters, enquote it
*/
assert(Value != NULL);
for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++)
/* nothing */;
return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE;
}
static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp)
{
TCHAR *p;
if (Section != NULL && _tcslen(Section) > 0) {
LocalBuffer[0] = '[';
save_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */
p = _tcsrchr(LocalBuffer, '\0');
assert(p != NULL);
*p++ = ']';
_tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */
ini_write(LocalBuffer, fp);
} /* if */
}
static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp)
{
TCHAR *p;
enum quote_option option = check_enquote(Value);
save_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */
p = _tcsrchr(LocalBuffer, '\0');
assert(p != NULL);
*p++ = '=';
save_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */
p = _tcsrchr(LocalBuffer, '\0');
assert(p != NULL);
_tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */
ini_write(LocalBuffer, fp);
}
static void write_quoted(const TCHAR *Value, INI_FILETYPE *fp)
{
TCHAR s[3];
int idx;
if (check_enquote(Value) == QUOTE_NONE) {
ini_write(Value, fp);
} else {
ini_write("\"", fp);
for (idx = 0; Value[idx] != '\0'; idx++) {
if (Value[idx] == '"') {
s[0] = '\\';
s[1] = Value[idx];
s[2] = '\0';
} else {
s[0] = Value[idx];
s[1] = '\0';
} /* if */
ini_write(s, fp);
} /* for */
ini_write("\"", fp);
} /* if */
}
/** ini_puts()
* \param Section the name of the section to write the string in
* \param Key the name of the entry to write, or NULL to erase all keys in the section
* \param Value a pointer to the buffer the string, or NULL to erase the key
* \param Filename the name and full path of the .ini file to write to
*
* \return 1 if successful, otherwise 0
*/
int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename)
{
INI_FILETYPE rfp;
INI_FILETYPE wfp;
TCHAR *sp, *ep;
TCHAR LocalBuffer[INI_BUFFERSIZE];
int len, match, count;
assert(Filename!=NULL);
if (!ini_openread(Filename, &rfp)) {
/* If the .ini file doesn't exist, make a new file */
if (Key!=NULL && Value!=NULL) {
if (!ini_openwrite(Filename, &wfp))
return 0;
writesection(LocalBuffer, Section, &wfp);
writekey(LocalBuffer, Key, Value, &wfp);
ini_close(&wfp);
} /* if */
return 1;
} /* if */
/* If parameters Key and Value are valid (so this is not an "erase" request)
* and the setting already exists and it already has the correct value, do
* nothing. This early bail-out avoids rewriting the INI file for no reason.
*/
if (Key!=NULL && Value!=NULL) {
match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer));
if (match && _tcscmp(LocalBuffer,Value)==0) {
ini_close(&rfp);
return 1;
} /* if */
/* key not found, or different value -> proceed (but rewind the input file first) */
ini_rewind(&rfp);
} /* if */
/* Get a temporary file name to copy to. Use the existing name, but with
* the last character set to a '~'.
*/
ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE);
if (!ini_openwrite(LocalBuffer, &wfp)) {
ini_close(&rfp);
return 0;
} /* if */
/* Move through the file one line at a time until a section is
* matched or until EOF. Copy to temp file as it is read.
*/
count = 0;
len = (Section != NULL) ? _tcslen(Section) : 0;
if (len > 0) {
do {
if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {
/* Failed to find section, so add one to the end */
if (Key!=NULL && Value!=NULL) {
ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */
writesection(LocalBuffer, Section, &wfp);
writekey(LocalBuffer, Key, Value, &wfp);
} /* if */
/* Clean up and rename */
ini_close(&rfp);
ini_close(&wfp);
ini_remove(Filename);
ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE);
ini_rename(LocalBuffer, Filename);
return 1;
} /* if */
/* Copy the line from source to dest, but not if this is the section that
* we are looking for and this section must be removed
*/
sp = skipleading(LocalBuffer);
ep = _tcschr(sp, ']');
match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0);
if (!match || Key!=NULL) {
/* Remove blank lines, but insert a blank line (possibly one that was
* removed on the previous iteration) before a new section. This creates
* "neat" INI files.
*/
if (_tcslen(sp) > 0) {
if (*sp == '[' && count > 0)
ini_write(INI_LINETERM, &wfp);
ini_write(sp, &wfp);
count++;
} /* if */
} /* if */
} while (!match);
} /* if */
/* Now that the section has been found, find the entry. Stop searching
* upon leaving the section's area. Copy the file as it is read
* and create an entry if one is not found.
*/
len = (Key!=NULL) ? _tcslen(Key) : 0;
for( ;; ) {
if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {
/* EOF without an entry so make one */
if (Key!=NULL && Value!=NULL) {
ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */
writekey(LocalBuffer, Key, Value, &wfp);
} /* if */
/* Clean up and rename */
ini_close(&rfp);
ini_close(&wfp);
ini_remove(Filename);
ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE);
ini_rename(LocalBuffer, Filename);
return 1;
} /* if */
sp = skipleading(LocalBuffer);
ep = _tcschr(sp, '='); /* Parse out the equal sign */
if (ep == NULL)
ep = _tcschr(sp, ':');
match = (ep != NULL && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0);
if ((Key!=NULL && match) || *sp == '[')
break; /* found the key, or found a new section */
/* in the section that we re-write, do not copy empty lines */
if (Key!=NULL && _tcslen(sp) > 0)
ini_write(sp, &wfp);
} /* for */
if (*sp == '[') {
/* found start of new section, the key was not in the specified
* section, so we add it just before the new section
*/
if (Key!=NULL && Value!=NULL) {
/* We cannot use "writekey()" here, because we need to preserve the
* contents of LocalBuffer.
*/
ini_write(Key, &wfp);
ini_write("=", &wfp);
write_quoted(Value, &wfp);
ini_write(INI_LINETERM INI_LINETERM, &wfp); /* put a blank line between the current and the next section */
} /* if */
/* write the new section header that we read previously */
ini_write(sp, &wfp);
} else {
/* We found the key; ignore the line just read (with the key and
* the current value) and write the key with the new value.
*/
if (Key!=NULL && Value!=NULL)
writekey(LocalBuffer, Key, Value, &wfp);
} /* if */
/* Copy the rest of the INI file (removing empty lines, except before a section) */
while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {
sp = skipleading(LocalBuffer);
if (_tcslen(sp) > 0) {
if (*sp == '[')
ini_write(INI_LINETERM, &wfp);
ini_write(sp, &wfp);
} /* if */
} /* while */
/* Clean up and rename */
ini_close(&rfp);
ini_close(&wfp);
ini_remove(Filename);
ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE);
ini_rename(LocalBuffer, Filename);
return 1;
}
/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */
#define ABS(v) ((v) < 0 ? -(v) : (v))
static void strreverse(TCHAR *str)
{
TCHAR t;
int i, j;
for (i = 0, j = _tcslen(str) - 1; i < j; i++, j--) {
t = str[i];
str[i] = str[j];
str[j] = t;
} /* for */
}
static void long2str(long value, TCHAR *str)
{
int i = 0;
long sign = value;
int n;
/* generate digits in reverse order */
do {
n = (int)(value % 10); /* get next lowest digit */
str[i++] = (TCHAR)(ABS(n) + '0'); /* handle case of negative digit */
} while (value /= 10); /* delete the lowest digit */
if (sign < 0)
str[i++] = '-';
str[i] = '\0';
strreverse(str);
}
/** ini_putl()
* \param Section the name of the section to write the value in
* \param Key the name of the entry to write, or NULL to erase all keys in the section
* \param Value the value to write
* \param Filename the name and full path of the .ini file to write to
*
* \return 1 if successful, otherwise 0
*/
int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename)
{
TCHAR str[32];
long2str(Value, str);
return ini_puts(Section, Key, str, Filename);
}
#endif /* !INI_READONLY */
#if defined PORTABLE_STRNICMP
int strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n)
{
register unsigned TCHAR c1, c2;
while (n-- != 0 && (*s1 || *s2)) {
c1 = *(const unsigned TCHAR *)s1++;
if ('a' <= c1 && c1 <= 'z')
c1 += ('A' - 'a');
c2 = *(const unsigned TCHAR *)s2++;
if ('a' <= c2 && c2 <= 'z')
c2 += ('A' - 'a');
if (c1 != c2)
return c1 - c2;
} /* while */
return 0;
}
#endif /* PORTABLE_STRNICMP */