/** * Copyright (c) 2019, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SBUF_PARSER_H #define SBUF_PARSER_H #include "sbuf.h" /** * Greedy Recursive Descent Parser in C * * Stop using strstr or regular expressions. This simple Recursive Descent Parser can be * used to handle complex grammars. * * For example: * parsing a query string form a uri * input: "file:///foo/bar_far.so.1?_blah1&_bar=barval5&_barfar" * expected output: * parsed query: _blah1 = * parsed query: _bar = barval5 * parsed query: _barfar = * * static int qmark(struct sbuf *buf) { * return sbuf_char(buf, '?'); * } * static int notandoreq(struct sbuf *buf) { * return sbuf_notchars(buf, "&="); * } * static int notand(struct sbuf *buf) { * return sbuf_notchar(buf, '&'); * } * * const char *name; * int nameLen; * const char *value; * int valueLen; * const char *data = "file:///foo/bar_far.so.1?_blah1&_bar=barval5&_barfar"; * * //initialize * sbuf_parser_init(&buf, data, strlen(data)); * * //parse until question mark * assert(sbuf_until(&buf, sbuf_any, qmark)); * * //parse each query * while(!sbuf_end(&buf)) { * //record where the name starts * name = sbuf_cur(&buf); * * //name is valid until '=' or '&' * assert(sbuf_many1(&buf, notandoreq)); * nameLen = sbuf_cur(&buf) - name; * * value = 0; * valueLen = 0; * //if the next char is a '=' then we also get a value * if(sbuf_char(&buf, '=')) { * value = sbuf_cur(&buf); * * //value is until the next query that starts with '&' * assert(sbuf_many1(&buf, notand)); * valueLen = sbuf_cur(&buf) - value; * } * //expect '&' or end * sbuf_char(&buf, '&'); * printf("parsed query: %.*s = %.*s\n", nameLen, name, valueLen, value); * } * */ //! init static __inline void sbuf_parser_init(struct sbuf* buf, const char *data, int dataLen) { sbuf_init(buf, 0, (void*)data, dataLen); } //! current postiion static __inline char *sbuf_cur(struct sbuf* buf) { return (char*)sbuf_head(buf); } //! look at the next character if the buffer is still valid static __inline int sbuf_peek(struct sbuf* buf, char* c) { if(!sbuf_valid(buf)) { return 0; } *c = *sbuf_cur(buf); return 1; } //! returns true if the buffer is ended static __inline int sbuf_end(struct sbuf* buf) { return sbuf_left(buf) == 0; } //! consume 1 char if its in string chars static __inline int sbuf_chars(struct sbuf *buf, const char *chars) { int i = 0; char c; if(!sbuf_peek(buf, &c)) { return 0; } for(i = 0; chars[i] != 0; ++i) { if(c == chars[i]) { sbuf_advance(buf, 1); return 1; } } return 0; } //! consume 1 char only if its not in string chars static __inline int sbuf_notchars(struct sbuf *buf, const char *chars) { int i = 0; char c; if(!sbuf_peek(buf, &c)) { return 0; } for(i = 0; chars[i] != 0; ++i) { if(c == chars[i]) { return 0; } } sbuf_advance(buf, 1); return 1; } //! consume only char t static __inline int sbuf_char(struct sbuf *buf, const char t) { char str[2] = {t, 0}; return sbuf_chars(buf, str); } //! consume any char except for t static __inline int sbuf_notchar(struct sbuf *buf, const char t) { char str[2] = {t, 0}; return sbuf_notchars(buf, str); } /** * consume any char */ static __inline int sbuf_any(struct sbuf* buf) { return sbuf_notchars(buf, ""); } /** * range is pairs of characters * * pairs are inclusive, start must be less then or equal then the end * * for example: AZaz09--.. * matches uppercase and lowercase letters, numbers, dashes and dots * */ static __inline int sbuf_range(struct sbuf *buf, const char *chars) { int i, j; char c; if(!sbuf_peek(buf, &c)) { return 0; } for(i = 0, j = 1; chars[i] != 0 && chars[j] != 0; i+=2,j+=2) { if(c >= chars[i] && c <= chars[j]) { sbuf_advance(buf, 1); return 1; } } return 0; } /** * greedly consume and match the entire string * empty string always succeeds without consuming any data */ static __inline int sbuf_string(struct sbuf *buf, const char *str) { int i = 0; for(i = 0; str[i] != 0; ++i) { if(!sbuf_char(buf, str[i])) { return 0; } } return 1; } /** * consumes until fails */ static __inline int sbuf_many(struct sbuf *buf, int(*consume)(struct sbuf *buf)) { if(!sbuf_valid(buf)) { return 0; } while(consume(buf)) {;} return 1; } /** * consumes until fails, must consume at least 1 */ static __inline int sbuf_many1(struct sbuf *buf, int(*consume)(struct sbuf *buf)) { if(!consume(buf)) { return 0; } sbuf_many(buf, consume); return 1; } /** * runs 'consume' until 'stop' succeeds * 'stop' must fail in such a way that it doesn't consume any data */ static __inline int sbuf_until(struct sbuf *buf, int(*consume)(struct sbuf *buf), int(*stop)(struct sbuf *buf)) { while(!stop(buf)) { if(!consume(buf)) { return 0; } } return 1; } /** * allows for backtracking, * @param parser, runs parser and only consume if it succeeds */ static __inline int sbuf_try(struct sbuf *buf, int(*parser)(struct sbuf *buf)) { struct sbuf tryp; sbuf_parser_init(&tryp, sbuf_cur(buf), sbuf_left(buf)); if(parser(&tryp)) { sbuf_advance(buf, sbuf_cur(&tryp) - sbuf_cur(buf)); return 1; } return 0; } #endif // SBUF_PARSER_H