rewritten ini parser for smaller size
This commit is contained in:
+111
-503
@@ -1,526 +1,134 @@
|
||||
|
||||
/* #line 1 "ini_parser.rl" */
|
||||
|
||||
/* Ragel constants block */
|
||||
#include "ini_parser.h"
|
||||
|
||||
// Ragel setup
|
||||
|
||||
/* #line 10 "ini_parser.c" */
|
||||
static const char _ini_actions[] = {
|
||||
0, 1, 1, 1, 2, 1, 3, 1,
|
||||
4, 1, 5, 1, 6, 1, 7, 1,
|
||||
8, 1, 9, 1, 10, 1, 11, 1,
|
||||
13, 2, 0, 4, 2, 12, 4
|
||||
enum nini_state {
|
||||
NINI_IDLE,
|
||||
NINI_SECTION,
|
||||
NINI_KEY,
|
||||
NINI_VALUE,
|
||||
NINI_COMMENT,
|
||||
};
|
||||
|
||||
static const char _ini_eof_actions[] = {
|
||||
0, 23, 5, 5, 15, 15, 15, 15,
|
||||
19, 19, 0, 0, 0, 0, 0, 0,
|
||||
0
|
||||
};
|
||||
static struct {
|
||||
uint8_t section_i;
|
||||
char section[INI_KEY_MAX];
|
||||
|
||||
static const int ini_start = 1;
|
||||
static const int ini_first_final = 12;
|
||||
static const int ini_error = 0;
|
||||
uint8_t key_i;
|
||||
char key[INI_KEY_MAX];
|
||||
|
||||
static const int ini_en_section = 2;
|
||||
static const int ini_en_keyvalue = 4;
|
||||
static const int ini_en_comment = 8;
|
||||
static const int ini_en_discard2eol = 10;
|
||||
static const int ini_en_main = 1;
|
||||
uint8_t value_i;
|
||||
char value[INI_VALUE_MAX];
|
||||
bool val_last_space;
|
||||
|
||||
IniParserCallback cb;
|
||||
void *userdata;
|
||||
enum nini_state state;
|
||||
} nini;
|
||||
|
||||
/* #line 10 "ini_parser.rl" */
|
||||
|
||||
|
||||
// Persistent state
|
||||
static int8_t cs = -1; //!< Ragel's Current State variable
|
||||
static uint32_t buff_i = 0; //!< Write pointer for the buffers
|
||||
static char value_quote = 0; //!< Quote character of the currently collected value
|
||||
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character
|
||||
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback
|
||||
static void *userdata = NULL; //!< Currently assigned user data for the callback
|
||||
|
||||
// Buffers
|
||||
static char keybuf[INI_KEY_MAX];
|
||||
static char secbuf[INI_KEY_MAX+10];
|
||||
static char valbuf[INI_VALUE_MAX];
|
||||
|
||||
// See header for doxygen!
|
||||
|
||||
void
|
||||
ini_parse_reset_partial(void)
|
||||
void ini_parse_begin(IniParserCallback callback, void *userData)
|
||||
{
|
||||
buff_i = 0;
|
||||
value_quote = 0;
|
||||
value_nextesc = false;
|
||||
ini_parse_reset();
|
||||
nini.cb = callback;
|
||||
nini.userdata = userData;
|
||||
}
|
||||
|
||||
void
|
||||
ini_parse_reset(void)
|
||||
void ini_parse(const char *data, size_t len)
|
||||
{
|
||||
ini_parse_reset_partial();
|
||||
keybuf[0] = secbuf[0] = valbuf[0] = 0;
|
||||
|
||||
/* #line 67 "ini_parser.c" */
|
||||
{
|
||||
cs = ini_start;
|
||||
}
|
||||
for (; len > 0; len--) {
|
||||
char c = *data++;
|
||||
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
|
||||
if (nini.state != NINI_VALUE && nini.state != NINI_COMMENT)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* #line 41 "ini_parser.rl" */
|
||||
switch (nini.state) {
|
||||
case NINI_IDLE:
|
||||
if (c == '[') {
|
||||
nini.state = NINI_SECTION;
|
||||
nini.section_i = 0;
|
||||
}
|
||||
else if (c == '#') {
|
||||
nini.state = NINI_COMMENT;
|
||||
}
|
||||
else {
|
||||
nini.state = NINI_KEY;
|
||||
nini.key_i = 0;
|
||||
nini.value_i = 0;
|
||||
nini.val_last_space = false;
|
||||
nini.key[nini.key_i++] = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case NINI_COMMENT:
|
||||
if (c == '\n' || c == '\r') {
|
||||
nini.state = NINI_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case NINI_SECTION:
|
||||
if (c == ']') {
|
||||
nini.section[nini.section_i] = 0;
|
||||
nini.state = NINI_COMMENT; // discard to EOL
|
||||
break;
|
||||
}
|
||||
else if (nini.section_i < INI_KEY_MAX - 1) {
|
||||
nini.section[nini.section_i++] = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case NINI_KEY:
|
||||
if (c == '=') {
|
||||
nini.key[nini.key_i] = 0;
|
||||
nini.state = NINI_VALUE;
|
||||
}
|
||||
else if (nini.key_i < INI_KEY_MAX - 1) {
|
||||
nini.key[nini.key_i++] = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case NINI_VALUE:
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
if (nini.value_i) nini.val_last_space = true;
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
nini.value[nini.value_i] = 0;
|
||||
nini.state = NINI_IDLE;
|
||||
nini.cb(nini.section, nini.key, nini.value, nini.userdata);
|
||||
break;
|
||||
default:
|
||||
if (nini.val_last_space && nini.value_i < INI_VALUE_MAX - 1) {
|
||||
nini.value[nini.value_i++] = ' ';
|
||||
}
|
||||
|
||||
if (nini.value_i < INI_VALUE_MAX - 1) {
|
||||
nini.value[nini.value_i++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ini_parser_error(const char* msg)
|
||||
void *ini_parse_end(void)
|
||||
{
|
||||
ini_error("Parser error: %s", msg);
|
||||
ini_parse_reset_partial();
|
||||
if (nini.state == NINI_VALUE) {
|
||||
nini.value[nini.value_i] = 0;
|
||||
nini.state = NINI_IDLE;
|
||||
nini.cb(nini.section, nini.key, nini.value, nini.userdata);
|
||||
}
|
||||
|
||||
return nini.userdata;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ini_parse_begin(IniParserCallback callback, void *userData)
|
||||
void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
|
||||
{
|
||||
keyCallback = callback;
|
||||
userdata = userData;
|
||||
ini_parse_reset();
|
||||
ini_parse_begin(callback, userData);
|
||||
ini_parse(text, len);
|
||||
ini_parse_end();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
*ini_parse_end(void)
|
||||
void ini_parse_reset(void)
|
||||
{
|
||||
ini_parse("\n", 1);
|
||||
if (keyCallback) {
|
||||
keyCallback = NULL;
|
||||
}
|
||||
|
||||
void *ud = userdata;
|
||||
userdata = NULL;
|
||||
return ud;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
|
||||
{
|
||||
ini_parse_begin(callback, userData);
|
||||
ini_parse(text, len);
|
||||
ini_parse_end();
|
||||
}
|
||||
|
||||
static void
|
||||
rtrim_buf(char *buf, int32_t end)
|
||||
{
|
||||
if (end > 0) {
|
||||
while ((uint8_t)buf[--end] < 33);
|
||||
end++; // go past the last character
|
||||
}
|
||||
|
||||
buf[end] = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ini_parse(const char *newstr, size_t len)
|
||||
{
|
||||
int32_t i;
|
||||
char c;
|
||||
bool isnl;
|
||||
bool isquot;
|
||||
|
||||
// Load new data to Ragel vars
|
||||
const uint8_t *p;
|
||||
const uint8_t *eof;
|
||||
const uint8_t *pe;
|
||||
|
||||
if (len == 0) while(newstr[++len] != 0); // alternative to strlen
|
||||
|
||||
p = (const uint8_t *) newstr;
|
||||
eof = NULL;
|
||||
pe = (const uint8_t *) (newstr + len);
|
||||
|
||||
// Init Ragel on the first run
|
||||
if (cs == -1) {
|
||||
ini_parse_reset();
|
||||
}
|
||||
|
||||
// The parser
|
||||
|
||||
/* #line 152 "ini_parser.c" */
|
||||
{
|
||||
const char *_acts;
|
||||
unsigned int _nacts;
|
||||
|
||||
if ( p == pe )
|
||||
goto _test_eof;
|
||||
if ( cs == 0 )
|
||||
goto _out;
|
||||
_resume:
|
||||
switch ( cs ) {
|
||||
case 1:
|
||||
switch( (*p) ) {
|
||||
case 32u: goto tr1;
|
||||
case 35u: goto tr3;
|
||||
case 58u: goto tr0;
|
||||
case 59u: goto tr3;
|
||||
case 61u: goto tr0;
|
||||
case 91u: goto tr4;
|
||||
}
|
||||
if ( (*p) < 9u ) {
|
||||
if ( (*p) <= 8u )
|
||||
goto tr0;
|
||||
} else if ( (*p) > 13u ) {
|
||||
if ( 14u <= (*p) && (*p) <= 31u )
|
||||
goto tr0;
|
||||
} else
|
||||
goto tr1;
|
||||
goto tr2;
|
||||
case 0:
|
||||
goto _out;
|
||||
case 12:
|
||||
goto tr0;
|
||||
case 2:
|
||||
switch( (*p) ) {
|
||||
case 9u: goto tr6;
|
||||
case 32u: goto tr6;
|
||||
case 93u: goto tr5;
|
||||
}
|
||||
if ( (*p) <= 31u )
|
||||
goto tr5;
|
||||
goto tr7;
|
||||
case 3:
|
||||
if ( (*p) == 93u )
|
||||
goto tr8;
|
||||
if ( (*p) > 8u ) {
|
||||
if ( 10u <= (*p) && (*p) <= 31u )
|
||||
goto tr5;
|
||||
} else
|
||||
goto tr5;
|
||||
goto tr7;
|
||||
case 13:
|
||||
goto tr5;
|
||||
case 4:
|
||||
switch( (*p) ) {
|
||||
case 10u: goto tr10;
|
||||
case 58u: goto tr11;
|
||||
case 61u: goto tr11;
|
||||
}
|
||||
goto tr9;
|
||||
case 5:
|
||||
switch( (*p) ) {
|
||||
case 9u: goto tr13;
|
||||
case 10u: goto tr14;
|
||||
case 13u: goto tr15;
|
||||
case 32u: goto tr13;
|
||||
}
|
||||
goto tr12;
|
||||
case 6:
|
||||
switch( (*p) ) {
|
||||
case 10u: goto tr14;
|
||||
case 13u: goto tr15;
|
||||
}
|
||||
goto tr12;
|
||||
case 14:
|
||||
goto tr10;
|
||||
case 7:
|
||||
if ( (*p) == 10u )
|
||||
goto tr14;
|
||||
goto tr10;
|
||||
case 8:
|
||||
switch( (*p) ) {
|
||||
case 10u: goto tr17;
|
||||
case 13u: goto tr18;
|
||||
}
|
||||
goto tr16;
|
||||
case 15:
|
||||
goto tr19;
|
||||
case 9:
|
||||
if ( (*p) == 10u )
|
||||
goto tr17;
|
||||
goto tr19;
|
||||
case 10:
|
||||
switch( (*p) ) {
|
||||
case 10u: goto tr21;
|
||||
case 13u: goto tr22;
|
||||
}
|
||||
goto tr20;
|
||||
case 16:
|
||||
goto tr23;
|
||||
case 11:
|
||||
if ( (*p) == 10u )
|
||||
goto tr21;
|
||||
goto tr23;
|
||||
}
|
||||
|
||||
tr23: cs = 0; goto _again;
|
||||
tr0: cs = 0; goto f0;
|
||||
tr5: cs = 0; goto f4;
|
||||
tr10: cs = 0; goto f7;
|
||||
tr19: cs = 0; goto f11;
|
||||
tr1: cs = 1; goto _again;
|
||||
tr6: cs = 2; goto _again;
|
||||
tr7: cs = 3; goto f5;
|
||||
tr9: cs = 4; goto f8;
|
||||
tr13: cs = 5; goto _again;
|
||||
tr11: cs = 5; goto f9;
|
||||
tr12: cs = 6; goto f10;
|
||||
tr15: cs = 7; goto _again;
|
||||
tr16: cs = 8; goto _again;
|
||||
tr18: cs = 9; goto _again;
|
||||
tr20: cs = 10; goto _again;
|
||||
tr22: cs = 11; goto _again;
|
||||
tr2: cs = 12; goto f1;
|
||||
tr3: cs = 12; goto f2;
|
||||
tr4: cs = 12; goto f3;
|
||||
tr8: cs = 13; goto f6;
|
||||
tr14: cs = 14; goto f10;
|
||||
tr17: cs = 15; goto f12;
|
||||
tr21: cs = 16; goto f13;
|
||||
|
||||
f5: _acts = _ini_actions + 1; goto execFuncs;
|
||||
f6: _acts = _ini_actions + 3; goto execFuncs;
|
||||
f4: _acts = _ini_actions + 5; goto execFuncs;
|
||||
f1: _acts = _ini_actions + 7; goto execFuncs;
|
||||
f8: _acts = _ini_actions + 9; goto execFuncs;
|
||||
f9: _acts = _ini_actions + 11; goto execFuncs;
|
||||
f10: _acts = _ini_actions + 13; goto execFuncs;
|
||||
f7: _acts = _ini_actions + 15; goto execFuncs;
|
||||
f12: _acts = _ini_actions + 17; goto execFuncs;
|
||||
f11: _acts = _ini_actions + 19; goto execFuncs;
|
||||
f13: _acts = _ini_actions + 21; goto execFuncs;
|
||||
f0: _acts = _ini_actions + 23; goto execFuncs;
|
||||
f3: _acts = _ini_actions + 25; goto execFuncs;
|
||||
f2: _acts = _ini_actions + 28; goto execFuncs;
|
||||
|
||||
execFuncs:
|
||||
_nacts = *_acts++;
|
||||
while ( _nacts-- > 0 ) {
|
||||
switch ( *_acts++ ) {
|
||||
case 0:
|
||||
/* #line 130 "ini_parser.rl" */
|
||||
{
|
||||
buff_i = 0;
|
||||
{cs = 2;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* #line 135 "ini_parser.rl" */
|
||||
{
|
||||
if (buff_i >= INI_KEY_MAX) {
|
||||
ini_parser_error("Section name too long");
|
||||
{cs = 10;goto _again;}
|
||||
}
|
||||
keybuf[buff_i++] = (*p);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* #line 143 "ini_parser.rl" */
|
||||
{
|
||||
// we need a separate buffer for the result, otherwise a failed
|
||||
// partial parse would corrupt the section string
|
||||
rtrim_buf(keybuf, buff_i);
|
||||
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c;
|
||||
secbuf[i] = 0;
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* #line 155 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in [section]");
|
||||
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
/* #line 162 "ini_parser.rl" */
|
||||
{
|
||||
buff_i = 0;
|
||||
keybuf[buff_i++] = (*p); // add the first char
|
||||
{cs = 4;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
/* #line 168 "ini_parser.rl" */
|
||||
{
|
||||
if (buff_i >= INI_KEY_MAX) {
|
||||
ini_parser_error("Key too long");
|
||||
{cs = 10;goto _again;}
|
||||
}
|
||||
keybuf[buff_i++] = (*p);
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
/* #line 176 "ini_parser.rl" */
|
||||
{
|
||||
rtrim_buf(keybuf, buff_i);
|
||||
|
||||
// --- Value begin ---
|
||||
buff_i = 0;
|
||||
value_quote = 0;
|
||||
value_nextesc = false;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
/* #line 185 "ini_parser.rl" */
|
||||
{
|
||||
isnl = ((*p) == '\r' || (*p) == '\n');
|
||||
isquot = ((*p) == '\'' || (*p) == '"');
|
||||
|
||||
// detect our starting quote
|
||||
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) {
|
||||
value_quote = (*p);
|
||||
goto valueCharDone;
|
||||
}
|
||||
|
||||
if (buff_i >= INI_VALUE_MAX) {
|
||||
ini_parser_error("Value too long");
|
||||
{cs = 10;goto _again;}
|
||||
}
|
||||
|
||||
// end of string - clean up and report
|
||||
if ((!value_nextesc && (*p) == value_quote) || isnl) {
|
||||
if (isnl && value_quote) {
|
||||
ini_parser_error("Unterminated string");
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
|
||||
// unquoted: trim from the end
|
||||
if (!value_quote) {
|
||||
rtrim_buf(valbuf, buff_i);
|
||||
} else {
|
||||
valbuf[buff_i] = 0;
|
||||
}
|
||||
|
||||
if (keyCallback) {
|
||||
keyCallback(secbuf, keybuf, valbuf, userdata);
|
||||
}
|
||||
|
||||
// we don't want to discard to eol if the string was terminated by eol
|
||||
// - would delete the next line
|
||||
|
||||
if (isnl) {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||
}
|
||||
|
||||
c = (*p);
|
||||
// escape...
|
||||
if (value_nextesc) {
|
||||
if ((*p) == 'n') c = '\n';
|
||||
else if ((*p) == 'r') c = '\r';
|
||||
else if ((*p) == 't') c = '\t';
|
||||
else if ((*p) == 'e') c = '\033';
|
||||
}
|
||||
|
||||
// collecting characters...
|
||||
if (value_nextesc || (*p) != '\\') { // is quoted, or is not a quoting backslash - literal character
|
||||
valbuf[buff_i++] = c;
|
||||
}
|
||||
|
||||
value_nextesc = (!value_nextesc && (*p) == '\\');
|
||||
valueCharDone:;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
/* #line 247 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in key=value");
|
||||
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
/* #line 257 "ini_parser.rl" */
|
||||
{ {cs = 1;goto _again;} }
|
||||
break;
|
||||
case 10:
|
||||
/* #line 258 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in comment");
|
||||
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
/* #line 265 "ini_parser.rl" */
|
||||
{ {cs = 1;goto _again;} }
|
||||
break;
|
||||
case 12:
|
||||
/* #line 273 "ini_parser.rl" */
|
||||
{ {cs = 8;goto _again;} }
|
||||
break;
|
||||
case 13:
|
||||
/* #line 276 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in root");
|
||||
{cs = 10;goto _again;}
|
||||
}
|
||||
break;
|
||||
/* #line 458 "ini_parser.c" */
|
||||
}
|
||||
}
|
||||
goto _again;
|
||||
|
||||
_again:
|
||||
if ( cs == 0 )
|
||||
goto _out;
|
||||
if ( ++p != pe )
|
||||
goto _resume;
|
||||
_test_eof: {}
|
||||
if ( p == eof )
|
||||
{
|
||||
const char *__acts = _ini_actions + _ini_eof_actions[cs];
|
||||
unsigned int __nacts = (unsigned int) *__acts++;
|
||||
while ( __nacts-- > 0 ) {
|
||||
switch ( *__acts++ ) {
|
||||
case 3:
|
||||
/* #line 155 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in [section]");
|
||||
if((*p) == '\n') {cs = 1; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;} else {cs = 10; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
/* #line 247 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in key=value");
|
||||
if((*p) == '\n') {cs = 1; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;} else {cs = 10; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;}
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
/* #line 258 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in comment");
|
||||
if((*p) == '\n') {cs = 1; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;} else {cs = 10; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;}
|
||||
}
|
||||
break;
|
||||
case 13:
|
||||
/* #line 276 "ini_parser.rl" */
|
||||
{
|
||||
ini_parser_error("Syntax error in root");
|
||||
{cs = 10; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;}
|
||||
}
|
||||
break;
|
||||
/* #line 517 "ini_parser.c" */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_out: {}
|
||||
}
|
||||
|
||||
/* #line 283 "ini_parser.rl" */
|
||||
|
||||
nini.state = NINI_IDLE;
|
||||
}
|
||||
|
||||
+6
-6
@@ -8,12 +8,12 @@
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
// toggleable logging func
|
||||
#ifdef DEBUG_INI
|
||||
#define ini_error(fmt, ...) dbg("! INI err: "#fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ini_error(fmt, ...)
|
||||
#endif
|
||||
//// toggleable logging func
|
||||
//#ifdef DEBUG_INI
|
||||
//#define ini_error(fmt, ...) dbg("! INI err: "#fmt, ##__VA_ARGS__)
|
||||
//#else
|
||||
//#define ini_error(fmt, ...)
|
||||
//#endif
|
||||
|
||||
// buffer sizes
|
||||
//#define INI_KEY_MAX 20
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
|
||||
/* Ragel constants block */
|
||||
#include "ini_parser.h"
|
||||
|
||||
// Ragel setup
|
||||
%%{
|
||||
machine ini;
|
||||
write data;
|
||||
alphtype unsigned char;
|
||||
}%%
|
||||
|
||||
// Persistent state
|
||||
static int8_t cs = -1; //!< Ragel's Current State variable
|
||||
static uint32_t buff_i = 0; //!< Write pointer for the buffers
|
||||
static char value_quote = 0; //!< Quote character of the currently collected value
|
||||
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character
|
||||
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback
|
||||
static void *userdata = NULL; //!< Currently assigned user data for the callback
|
||||
|
||||
// Buffers
|
||||
static char keybuf[INI_KEY_MAX];
|
||||
static char secbuf[INI_KEY_MAX];
|
||||
static char valbuf[INI_VALUE_MAX];
|
||||
|
||||
// See header for doxygen!
|
||||
|
||||
void
|
||||
ini_parse_reset_partial(void)
|
||||
{
|
||||
buff_i = 0;
|
||||
value_quote = 0;
|
||||
value_nextesc = false;
|
||||
}
|
||||
|
||||
void
|
||||
ini_parse_reset(void)
|
||||
{
|
||||
ini_parse_reset_partial();
|
||||
keybuf[0] = secbuf[0] = valbuf[0] = 0;
|
||||
%% write init;
|
||||
}
|
||||
|
||||
void
|
||||
ini_parser_error(const char* msg)
|
||||
{
|
||||
ini_error("Parser error: %s", msg);
|
||||
ini_parse_reset_partial();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ini_parse_begin(IniParserCallback callback, void *userData)
|
||||
{
|
||||
keyCallback = callback;
|
||||
userdata = userData;
|
||||
ini_parse_reset();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
*ini_parse_end(void)
|
||||
{
|
||||
ini_parse("\n", 1);
|
||||
if (keyCallback) {
|
||||
keyCallback = NULL;
|
||||
}
|
||||
|
||||
void *ud = userdata;
|
||||
userdata = NULL;
|
||||
return ud;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
|
||||
{
|
||||
ini_parse_begin(callback, userData);
|
||||
ini_parse(text, len);
|
||||
ini_parse_end();
|
||||
}
|
||||
|
||||
static void
|
||||
rtrim_buf(char *buf, int32_t end)
|
||||
{
|
||||
if (end > 0) {
|
||||
while ((uint8_t)buf[--end] < 33);
|
||||
end++; // go past the last character
|
||||
}
|
||||
|
||||
buf[end] = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ini_parse(const char *newstr, size_t len)
|
||||
{
|
||||
int32_t i;
|
||||
char c;
|
||||
bool isnl;
|
||||
bool isquot;
|
||||
|
||||
// Load new data to Ragel vars
|
||||
const uint8_t *p;
|
||||
const uint8_t *eof;
|
||||
const uint8_t *pe;
|
||||
|
||||
if (len == 0) while(newstr[++len] != 0); // alternative to strlen
|
||||
|
||||
p = (const uint8_t *) newstr;
|
||||
eof = NULL;
|
||||
pe = (const uint8_t *) (newstr + len);
|
||||
|
||||
// Init Ragel on the first run
|
||||
if (cs == -1) {
|
||||
ini_parse_reset();
|
||||
}
|
||||
|
||||
// The parser
|
||||
%%{
|
||||
#/ *
|
||||
ispace = [ \t]; # inline space
|
||||
wchar = any - 0..8 - 10..31;
|
||||
#apos = '\'';
|
||||
#quot = '\"';
|
||||
nonl = [^\r\n];
|
||||
nl = '\r'? '\n';
|
||||
|
||||
# ---- [SECTION] ----
|
||||
|
||||
action sectionStart {
|
||||
buff_i = 0;
|
||||
fgoto section;
|
||||
}
|
||||
|
||||
action sectionChar {
|
||||
if (buff_i >= INI_KEY_MAX) {
|
||||
ini_parser_error("Section name too long");
|
||||
fgoto discard2eol;
|
||||
}
|
||||
keybuf[buff_i++] = fc;
|
||||
}
|
||||
|
||||
action sectionEnd {
|
||||
// we need a separate buffer for the result, otherwise a failed
|
||||
// partial parse would corrupt the section string
|
||||
rtrim_buf(keybuf, buff_i);
|
||||
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c;
|
||||
secbuf[i] = 0;
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
section :=
|
||||
(
|
||||
ispace* <: ((wchar - ']')+ @sectionChar) ']' @sectionEnd
|
||||
) $!{
|
||||
ini_parser_error("Syntax error in [section]");
|
||||
if(fc == '\n') fgoto main; else fgoto discard2eol;
|
||||
};
|
||||
|
||||
# ---- KEY=VALUE ----
|
||||
|
||||
action keyStart {
|
||||
buff_i = 0;
|
||||
keybuf[buff_i++] = fc; // add the first char
|
||||
fgoto keyvalue;
|
||||
}
|
||||
|
||||
action keyChar {
|
||||
if (buff_i >= INI_KEY_MAX) {
|
||||
ini_parser_error("Key too long");
|
||||
fgoto discard2eol;
|
||||
}
|
||||
keybuf[buff_i++] = fc;
|
||||
}
|
||||
|
||||
action keyEnd {
|
||||
rtrim_buf(keybuf, buff_i);
|
||||
|
||||
// --- Value begin ---
|
||||
buff_i = 0;
|
||||
value_quote = 0;
|
||||
value_nextesc = false;
|
||||
}
|
||||
|
||||
action valueChar {
|
||||
isnl = (fc == '\r' || fc == '\n');
|
||||
isquot = (fc == '\'' || fc == '"');
|
||||
|
||||
// detect our starting quote
|
||||
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) {
|
||||
value_quote = fc;
|
||||
goto valueCharDone;
|
||||
}
|
||||
|
||||
if (buff_i >= INI_VALUE_MAX) {
|
||||
ini_parser_error("Value too long");
|
||||
fgoto discard2eol;
|
||||
}
|
||||
|
||||
// end of string - clean up and report
|
||||
if ((!value_nextesc && fc == value_quote) || isnl) {
|
||||
if (isnl && value_quote) {
|
||||
ini_parser_error("Unterminated string");
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
// unquoted: trim from the end
|
||||
if (!value_quote) {
|
||||
rtrim_buf(valbuf, buff_i);
|
||||
} else {
|
||||
valbuf[buff_i] = 0;
|
||||
}
|
||||
|
||||
if (keyCallback) {
|
||||
keyCallback(secbuf, keybuf, valbuf, userdata);
|
||||
}
|
||||
|
||||
// we don't want to discard to eol if the string was terminated by eol
|
||||
// - would delete the next line
|
||||
|
||||
if (isnl) fgoto main; else fgoto discard2eol;
|
||||
}
|
||||
|
||||
c = fc;
|
||||
// escape...
|
||||
if (value_nextesc) {
|
||||
if (fc == 'n') c = '\n';
|
||||
else if (fc == 'r') c = '\r';
|
||||
else if (fc == 't') c = '\t';
|
||||
else if (fc == 'e') c = '\033';
|
||||
}
|
||||
|
||||
// collecting characters...
|
||||
if (value_nextesc || fc != '\\') { // is quoted, or is not a quoting backslash - literal character
|
||||
valbuf[buff_i++] = c;
|
||||
}
|
||||
|
||||
value_nextesc = (!value_nextesc && fc == '\\');
|
||||
valueCharDone:;
|
||||
}
|
||||
|
||||
# use * for key, first char is already consumed.
|
||||
keyvalue :=
|
||||
(
|
||||
([^\n=:]* @keyChar %keyEnd)
|
||||
[=:] ispace* <: nonl* @valueChar nl @valueChar
|
||||
) $!{
|
||||
ini_parser_error("Syntax error in key=value");
|
||||
if(fc == '\n') fgoto main; else fgoto discard2eol;
|
||||
};
|
||||
|
||||
# ---- COMMENT ----
|
||||
|
||||
comment :=
|
||||
(
|
||||
nonl* nl
|
||||
@{ fgoto main; }
|
||||
) $!{
|
||||
ini_parser_error("Syntax error in comment");
|
||||
if(fc == '\n') fgoto main; else fgoto discard2eol;
|
||||
};
|
||||
|
||||
# ---- CLEANUP ----
|
||||
|
||||
discard2eol := nonl* nl @{ fgoto main; };
|
||||
|
||||
# ---- ROOT ----
|
||||
|
||||
main :=
|
||||
(space*
|
||||
(
|
||||
'[' @sectionStart |
|
||||
[#;] @{ fgoto comment; } |
|
||||
(wchar - [\t =:]) @keyStart
|
||||
)
|
||||
) $!{
|
||||
ini_parser_error("Syntax error in root");
|
||||
fgoto discard2eol;
|
||||
};
|
||||
|
||||
write exec;
|
||||
#*/
|
||||
}%%
|
||||
}
|
||||
Reference in New Issue
Block a user