/* 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; #*/ }%% }