parent
4e285694d1
commit
ba2b73a287
@ -1 +1 @@ |
|||||||
Subproject commit e4ecf0724e36c828be5222eddce58a6a5cd2386f |
Subproject commit d6651ca0d11b7950078247b8305f5b23a6be6c6c |
@ -0,0 +1,526 @@ |
|||||||
|
|
||||||
|
/* #line 1 "user/ini_parser.rl" */ |
||||||
|
|
||||||
|
/* Ragel constants block */ |
||||||
|
#include "ini_parser.h" |
||||||
|
|
||||||
|
// Ragel setup
|
||||||
|
|
||||||
|
/* #line 10 "user/ini_parser.c" */ |
||||||
|
static const char _ini_actions[] ESP_CONST_DATA = { |
||||||
|
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 |
||||||
|
}; |
||||||
|
|
||||||
|
static const char _ini_eof_actions[] ESP_CONST_DATA = { |
||||||
|
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 "user/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]; |
||||||
|
static char valbuf[INI_VALUE_MAX]; |
||||||
|
|
||||||
|
// See header for doxygen!
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
ini_parse_reset_partial(void) |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
value_quote = 0; |
||||||
|
value_nextesc = false; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
ini_parse_reset(void) |
||||||
|
{ |
||||||
|
ini_parse_reset_partial(); |
||||||
|
keybuf[0] = secbuf[0] = valbuf[0] = 0; |
||||||
|
|
||||||
|
/* #line 67 "user/ini_parser.c" */ |
||||||
|
{ |
||||||
|
cs = ini_start; |
||||||
|
} |
||||||
|
|
||||||
|
/* #line 41 "user/ini_parser.rl" */ |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
ini_parser_error(const char* msg) |
||||||
|
{ |
||||||
|
ini_error("Parser error: %s", msg); |
||||||
|
ini_parse_reset_partial(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
ini_parse_begin(IniParserCallback callback, void *userData) |
||||||
|
{ |
||||||
|
keyCallback = callback; |
||||||
|
userdata = userData; |
||||||
|
ini_parse_reset(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
*ini_parse_end(void) |
||||||
|
{ |
||||||
|
ini_parse("\n", 1); |
||||||
|
if (keyCallback) { |
||||||
|
keyCallback = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void *ud = userdata; |
||||||
|
userdata = NULL; |
||||||
|
return ud; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
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 ICACHE_FLASH_ATTR |
||||||
|
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 ICACHE_FLASH_ATTR |
||||||
|
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 "user/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 "user/ini_parser.rl" */ |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
{cs = 2;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
/* #line 135 "user/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 "user/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 "user/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 "user/ini_parser.rl" */ |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
keybuf[buff_i++] = (*p); // add the first char
|
||||||
|
{cs = 4;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 5: |
||||||
|
/* #line 168 "user/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 "user/ini_parser.rl" */ |
||||||
|
{ |
||||||
|
rtrim_buf(keybuf, buff_i); |
||||||
|
|
||||||
|
// --- Value begin ---
|
||||||
|
buff_i = 0; |
||||||
|
value_quote = 0; |
||||||
|
value_nextesc = false; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 7: |
||||||
|
/* #line 185 "user/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 "user/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 "user/ini_parser.rl" */ |
||||||
|
{ {cs = 1;goto _again;} } |
||||||
|
break; |
||||||
|
case 10: |
||||||
|
/* #line 258 "user/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 "user/ini_parser.rl" */ |
||||||
|
{ {cs = 1;goto _again;} } |
||||||
|
break; |
||||||
|
case 12: |
||||||
|
/* #line 273 "user/ini_parser.rl" */ |
||||||
|
{ {cs = 8;goto _again;} } |
||||||
|
break; |
||||||
|
case 13: |
||||||
|
/* #line 276 "user/ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in root"); |
||||||
|
{cs = 10;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
/* #line 458 "user/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 "user/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 "user/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 "user/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 "user/ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in root"); |
||||||
|
{cs = 10; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
/* #line 517 "user/ini_parser.c" */ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_out: {} |
||||||
|
} |
||||||
|
|
||||||
|
/* #line 283 "user/ini_parser.rl" */ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
#ifndef INIPARSE_STREAM_H |
||||||
|
#define INIPARSE_STREAM_H |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
|
||||||
|
#ifdef DEBUG_INI |
||||||
|
#define ini_error(fmt, ...) error("[INI] "#fmt, ##__VA_ARGS__) |
||||||
|
#else |
||||||
|
#define ini_error(fmt, ...) |
||||||
|
#endif |
||||||
|
|
||||||
|
// buffer sizes
|
||||||
|
#define INI_KEY_MAX 64 |
||||||
|
#define INI_VALUE_MAX 256 |
||||||
|
|
||||||
|
/**
|
||||||
|
* INI parser callback, called for each found key-value pair. |
||||||
|
* |
||||||
|
* @param section - current section, empty string for global keys |
||||||
|
* @param key - found key (trimmed of whitespace) |
||||||
|
* @param value - value, trimmed of quotes or whitespace |
||||||
|
* @param userData - opaque user data pointer, general purpose |
||||||
|
*/ |
||||||
|
typedef void (*IniParserCallback)(const char *section, const char *key, const char *value, void *userData); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin parsing a stream |
||||||
|
* |
||||||
|
* @param callback - key callback to assign |
||||||
|
* @param userData - optional user data that willb e passed to the callback |
||||||
|
*/ |
||||||
|
void ini_parse_begin(IniParserCallback callback, void *userData); |
||||||
|
|
||||||
|
/**
|
||||||
|
* End parse stream. |
||||||
|
* Flushes what remains in the buffer and removes callback. |
||||||
|
* |
||||||
|
* @returns userData or NULL if none |
||||||
|
*/ |
||||||
|
void* ini_parse_end(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a string (needn't be complete line or file) |
||||||
|
* |
||||||
|
* @param data - string to parse |
||||||
|
* @param len - string length (0 = use strlen) |
||||||
|
*/ |
||||||
|
void ini_parse(const char *data, size_t len); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a complete file loaded to string |
||||||
|
* |
||||||
|
* @param text - entire file as string |
||||||
|
* @param len - file length (0 = use strlen) |
||||||
|
* @param callback - key callback |
||||||
|
* @param userData - optional user data for key callback |
||||||
|
*/ |
||||||
|
void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly reset the parser |
||||||
|
*/ |
||||||
|
void ini_parse_reset(void); |
||||||
|
|
||||||
|
#endif // INIPARSE_STREAM_H
|
@ -0,0 +1,284 @@ |
|||||||
|
|
||||||
|
/* 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 ICACHE_FLASH_ATTR |
||||||
|
ini_parse_reset_partial(void) |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
value_quote = 0; |
||||||
|
value_nextesc = false; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
ini_parse_reset(void) |
||||||
|
{ |
||||||
|
ini_parse_reset_partial(); |
||||||
|
keybuf[0] = secbuf[0] = valbuf[0] = 0; |
||||||
|
%% write init; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
ini_parser_error(const char* msg) |
||||||
|
{ |
||||||
|
ini_error("Parser error: %s", msg); |
||||||
|
ini_parse_reset_partial(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
ini_parse_begin(IniParserCallback callback, void *userData) |
||||||
|
{ |
||||||
|
keyCallback = callback; |
||||||
|
userdata = userData; |
||||||
|
ini_parse_reset(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
*ini_parse_end(void) |
||||||
|
{ |
||||||
|
ini_parse("\n", 1); |
||||||
|
if (keyCallback) { |
||||||
|
keyCallback = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void *ud = userdata; |
||||||
|
userdata = NULL; |
||||||
|
return ud; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
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 ICACHE_FLASH_ATTR |
||||||
|
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 ICACHE_FLASH_ATTR |
||||||
|
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