| /** | |
| * 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 |