parent
ed7a4c80a0
commit
4b8d24ae0f
@ -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 const int ini_start = 1; |
||||
static const int ini_first_final = 12; |
||||
static const int ini_error = 0; |
||||
|
||||
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; |
||||
|
||||
|
||||
/* #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) |
||||
{ |
||||
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; |
||||
static struct { |
||||
uint8_t section_i; |
||||
char section[INI_KEY_MAX]; |
||||
|
||||
/* #line 67 "ini_parser.c" */ |
||||
{ |
||||
cs = ini_start; |
||||
} |
||||
uint8_t key_i; |
||||
char key[INI_KEY_MAX]; |
||||
|
||||
/* #line 41 "ini_parser.rl" */ |
||||
} |
||||
|
||||
void |
||||
ini_parser_error(const char* msg) |
||||
{ |
||||
ini_error("Parser error: %s", msg); |
||||
ini_parse_reset_partial(); |
||||
} |
||||
uint8_t value_i; |
||||
char value[INI_VALUE_MAX]; |
||||
bool val_last_space; |
||||
|
||||
IniParserCallback cb; |
||||
void *userdata; |
||||
enum nini_state state; |
||||
} nini; |
||||
|
||||
void |
||||
ini_parse_begin(IniParserCallback callback, void *userData) |
||||
void ini_parse_begin(IniParserCallback callback, void *userData) |
||||
{ |
||||
keyCallback = callback; |
||||
userdata = userData; |
||||
ini_parse_reset(); |
||||
nini.cb = callback; |
||||
nini.userdata = userData; |
||||
} |
||||
|
||||
|
||||
void |
||||
*ini_parse_end(void) |
||||
void ini_parse(const char *data, size_t len) |
||||
{ |
||||
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; |
||||
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; |
||||
} |
||||
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); |
||||
switch (nini.state) { |
||||
case NINI_IDLE: |
||||
if (c == '[') { |
||||
nini.state = NINI_SECTION; |
||||
nini.section_i = 0; |
||||
} |
||||
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;} |
||||
else if (c == '#') { |
||||
nini.state = NINI_COMMENT; |
||||
} |
||||
keybuf[buff_i++] = (*p); |
||||
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 6: |
||||
/* #line 176 "ini_parser.rl" */ |
||||
{ |
||||
rtrim_buf(keybuf, buff_i); |
||||
|
||||
// --- Value begin ---
|
||||
buff_i = 0; |
||||
value_quote = 0; |
||||
value_nextesc = false; |
||||
case NINI_COMMENT: |
||||
if (c == '\n' || c == '\r') { |
||||
nini.state = NINI_IDLE; |
||||
} |
||||
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;} } |
||||
case NINI_SECTION: |
||||
if (c == ']') { |
||||
nini.section[nini.section_i] = 0; |
||||
nini.state = NINI_COMMENT; // discard to EOL
|
||||
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;} |
||||
else if (nini.section_i < INI_KEY_MAX - 1) { |
||||
nini.section[nini.section_i++] = c; |
||||
} |
||||
break; |
||||
/* #line 458 "ini_parser.c" */ |
||||
|
||||
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; |
||||
} |
||||
goto _again; |
||||
break; |
||||
|
||||
_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;} |
||||
} |
||||
case NINI_VALUE: |
||||
switch (c) { |
||||
case ' ': |
||||
case '\t': |
||||
if (nini.value_i) nini.val_last_space = true; |
||||
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;} |
||||
} |
||||
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; |
||||
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;} |
||||
default: |
||||
if (nini.val_last_space && nini.value_i < INI_VALUE_MAX - 1) { |
||||
nini.value[nini.value_i++] = ' '; |
||||
} |
||||
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;} |
||||
|
||||
if (nini.value_i < INI_VALUE_MAX - 1) { |
||||
nini.value[nini.value_i++] = c; |
||||
} |
||||
break; |
||||
/* #line 517 "ini_parser.c" */ |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
_out: {} |
||||
void *ini_parse_end(void) |
||||
{ |
||||
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); |
||||
} |
||||
|
||||
/* #line 283 "ini_parser.rl" */ |
||||
return nini.userdata; |
||||
} |
||||
|
||||
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(); |
||||
} |
||||
|
||||
void ini_parse_reset(void) |
||||
{ |
||||
nini.state = NINI_IDLE; |
||||
} |
||||
|
@ -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; |
||||
#*/ |
||||
}%% |
||||
} |
Loading…
Reference in new issue