ini upload and parse (no loading to conf yet)
This commit is contained in:
@@ -146,6 +146,9 @@ set(SOURCE_FILES
|
||||
user/jstring.c
|
||||
user/jstring.h
|
||||
user/character_sets.h
|
||||
user/ini_parser.h
|
||||
user/ini_parser.c
|
||||
user/ini_parser.rl
|
||||
user/utf8.h
|
||||
user/utf8.c
|
||||
user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c)
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
echo "-- Building parser from Ragel source --"
|
||||
|
||||
ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c
|
||||
ragel -L -G0 user/ini_parser.rl -o user/ini_parser.c
|
||||
|
||||
sed -i "s/static const char _ansi_actions\[\]/static const char _ansi_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
|
||||
sed -i "s/static const char _ansi_eof_actions\[\]/static const char _ansi_eof_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
|
||||
|
||||
sed -i "s/static const char _ini_actions\[\]/static const char _ini_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
|
||||
sed -i "s/static const char _ini_eof_actions\[\]/static const char _ini_eof_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
|
||||
|
||||
@@ -40,6 +40,7 @@ ESP_SPI_FLASH_SIZE_K = 1024
|
||||
|
||||
GLOBAL_CFLAGS = \
|
||||
-DASYNC_LOG=1 \
|
||||
-DDEBUG_INI=0 \
|
||||
-DDEBUG_D2D=0 \
|
||||
-DDEBUG_ROUTER=0 \
|
||||
-DDEBUG_CAPTDNS=0 \
|
||||
|
||||
+1
-1
Submodule libesphttpd updated: e4ecf0724e...d6651ca0d1
+59
-2
@@ -10,6 +10,7 @@ Cgi/template routines for configuring non-wifi settings
|
||||
#include "version.h"
|
||||
#include "screen.h"
|
||||
#include "config_xmacros.h"
|
||||
#include "ini_parser.h"
|
||||
|
||||
#define SET_REDIR_SUC "/cfg/system"
|
||||
|
||||
@@ -155,10 +156,17 @@ cgiPersistExport(HttpdConnData *connData)
|
||||
httpdSend(connData, buff, -1);
|
||||
}
|
||||
|
||||
// do not export SSID if unchanged - embeds unique ID that should
|
||||
// not be overwritten in target.
|
||||
|
||||
char defSSID[20];
|
||||
sprintf(defSSID, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]);
|
||||
|
||||
bool quoted;
|
||||
#define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
|
||||
if (allow) { \
|
||||
do { if (allow) { \
|
||||
xget(buff, deref XSTRUCT->name); \
|
||||
if (streq(#name, "ap_ssid") && streq(buff, defSSID)) break; \
|
||||
\
|
||||
quoted = false; \
|
||||
quoted |= streq(#type, "char"); \
|
||||
@@ -173,7 +181,7 @@ cgiPersistExport(HttpdConnData *connData)
|
||||
} else { \
|
||||
httpdSend(connData, buff, -1); \
|
||||
} \
|
||||
}
|
||||
} } while(0);
|
||||
|
||||
#define admin 1
|
||||
#define tpl 1
|
||||
@@ -207,3 +215,52 @@ cgiPersistExport(HttpdConnData *connData)
|
||||
connData->cgiData = (void *) (step + 1);
|
||||
return HTTPD_CGI_MORE;
|
||||
}
|
||||
|
||||
|
||||
void iniCb(const char *section, const char *key, const char *value, void *userData)
|
||||
{
|
||||
dbg(">>> SET: [%s] %s = %s", section, key, value);
|
||||
}
|
||||
|
||||
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR
|
||||
postRecvHdl(HttpdConnData *connData, char *data, int len)
|
||||
{
|
||||
ini_parse(data, (size_t) len);
|
||||
ini_parse_end();
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import settings from INI
|
||||
*
|
||||
* @param connData
|
||||
* @return status
|
||||
*/
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR
|
||||
cgiPersistImport(HttpdConnData *connData)
|
||||
{
|
||||
if (connData->conn == NULL) {
|
||||
//Connection aborted. Clean up.
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
httpdStartResponse(connData, 200);
|
||||
httpdHeader(connData, "Content-Type", "text/plain");
|
||||
httpdEndHeaders(connData);
|
||||
|
||||
char *start = strstr(connData->post->buff, "\r\n\r\n");
|
||||
if (start == NULL) {
|
||||
error("Malformed attachment POST!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ini_parse_begin(iniCb, NULL);
|
||||
ini_parse(start, (size_t) connData->post->buffLen - (start - connData->post->buff));
|
||||
|
||||
connData->recvHdl = postRecvHdl;
|
||||
|
||||
end:
|
||||
// TODO redirect
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
@@ -7,5 +7,6 @@ httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData);
|
||||
httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData);
|
||||
httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData);
|
||||
httpd_cgi_state cgiPersistExport(HttpdConnData *connData);
|
||||
httpd_cgi_state cgiPersistImport(HttpdConnData *connData);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
#*/
|
||||
}%%
|
||||
}
|
||||
@@ -132,6 +132,7 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
|
||||
ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"),
|
||||
ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams),
|
||||
ROUTE_CGI("/cfg/system/export", cgiPersistExport),
|
||||
ROUTE_CGI("/cfg/system/import", cgiPersistImport),
|
||||
ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults),
|
||||
ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults),
|
||||
ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard),
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ enum pwlock {
|
||||
PWLOCK_MAX = 5,
|
||||
};
|
||||
|
||||
//....Type................Name..Suffix...............Deref..XGET..........Cast..XSET.........................NOTIFY....Allow
|
||||
//....Type................Name..Suffix...............Deref..XGET............XSET.........................NOTIFY....Allow
|
||||
// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays)
|
||||
#define XTABLE_SYSCONF \
|
||||
X(u32, uart_baudrate, /**/, /**/, xget_dec, xset_sys_baudrate, NULL, uart_changed=true, 1) \
|
||||
|
||||
Reference in New Issue
Block a user