From bf721f718ed2ca3e5d409a2f6b48312ee95d2e4e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 14 Aug 2013 01:19:33 +0200 Subject: Implement new lexer that is not generated by flex to reduce code size --- CMakeLists.txt | 1 - FindFLEX.cmake | 120 -------------- src/CMakeLists.txt | 3 +- src/config.c | 11 +- src/config.l | 244 ---------------------------- src/lex.c | 457 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lex.h | 41 +++++ src/types.h | 1 + 8 files changed, 505 insertions(+), 373 deletions(-) delete mode 100644 FindFLEX.cmake delete mode 100644 src/config.l create mode 100644 src/lex.c create mode 100644 src/lex.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f7160eb..de04693 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,6 @@ set(CMAKE_MODULE_PATH ${FASTD_SOURCE_DIR}) set(FASTD_VERSION "v8+") find_package(BISON 2.5 REQUIRED) -find_package(FLEX REQUIRED) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads) diff --git a/FindFLEX.cmake b/FindFLEX.cmake deleted file mode 100644 index fe83369..0000000 --- a/FindFLEX.cmake +++ /dev/null @@ -1,120 +0,0 @@ -#============================================================================= -# Copyright 2009 Kitware, Inc. -# Copyright 2006 Tristan Carel -# -# 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 names of Kitware, Inc., the Insight Software Consortium, -# nor the names of their contributors may be used to endorse or promote -# products derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER 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. -# -# ------------------------------------------------------------------------------ -# -# The above copyright and license notice applies to distributions of -# CMake in source and binary form. Some source files contain additional -# notices of original copyright by their contributors; see each source -# for details. Third-party software packages supplied with CMake under -# compatible licenses provide their own copyright notices documented in -# corresponding subdirectories. -#============================================================================= - -FIND_PROGRAM(FLEX_EXECUTABLE flex DOC "path to the flex executable") -MARK_AS_ADVANCED(FLEX_EXECUTABLE) - -FIND_LIBRARY(FL_LIBRARY NAMES fl - DOC "Path to the fl library") - -FIND_PATH(FLEX_INCLUDE_DIR FlexLexer.h - DOC "Path to the flex headers") - -MARK_AS_ADVANCED(FL_LIBRARY FLEX_INCLUDE_DIR) - -SET(FLEX_INCLUDE_DIRS ${FLEX_INCLUDE_DIR}) -SET(FLEX_LIBRARIES ${FL_LIBRARY}) - -IF(FLEX_EXECUTABLE) - - EXECUTE_PROCESS(COMMAND ${FLEX_EXECUTABLE} --version - OUTPUT_VARIABLE FLEX_version_output - ERROR_VARIABLE FLEX_version_error - RESULT_VARIABLE FLEX_version_result - OUTPUT_STRIP_TRAILING_WHITESPACE) - IF(NOT ${FLEX_version_result} EQUAL 0) - IF(FLEX_FIND_REQUIRED) - MESSAGE(SEND_ERROR "Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}") - ELSE() - MESSAGE("Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}\nFLEX_VERSION will not be available") - ENDIF() - ELSE() - STRING(REGEX REPLACE "^flex (.*)$" "\\1" - FLEX_VERSION "${FLEX_version_output}") - ENDIF() - - #============================================================ - # FLEX_TARGET (public macro) - #============================================================ - # - MACRO(FLEX_TARGET Name Input Output) - SET(FLEX_TARGET_usage "FLEX_TARGET( [COMPILE_FLAGS ]") - IF(${ARGC} GREATER 3) - IF(${ARGC} EQUAL 5) - IF("${ARGV3}" STREQUAL "COMPILE_FLAGS") - SET(FLEX_EXECUTABLE_opts "${ARGV4}") - SEPARATE_ARGUMENTS(FLEX_EXECUTABLE_opts) - ELSE() - MESSAGE(SEND_ERROR ${FLEX_TARGET_usage}) - ENDIF() - ELSE() - MESSAGE(SEND_ERROR ${FLEX_TARGET_usage}) - ENDIF() - ENDIF() - - STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\2" _fileext "${Output}") - STRING(REPLACE "c" "h" _fileext ${_fileext}) - STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\1${_fileext}" - OutputHeader "${Output}") - - ADD_CUSTOM_COMMAND(OUTPUT ${Output} ${OutputHeader} - COMMAND ${FLEX_EXECUTABLE} - ARGS ${FLEX_EXECUTABLE_opts} -o${Output} --header-file=${OutputHeader} ${Input} - DEPENDS ${Input} - COMMENT "[FLEX][${Name}] Building scanner with flex ${FLEX_VERSION}" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - - SET(FLEX_${Name}_DEFINED TRUE) - SET(FLEX_${Name}_OUTPUTS ${Output} ${OutputHeader}) - SET(FLEX_${Name}_OUTPUT_HEADER ${OutputHeader}) - SET(FLEX_${Name}_INPUT ${Input}) - SET(FLEX_${Name}_COMPILE_FLAGS ${FLEX_EXECUTABLE_opts}) - ENDMACRO(FLEX_TARGET) - #============================================================ - - -ENDIF(FLEX_EXECUTABLE) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(FLEX REQUIRED_VARS FLEX_EXECUTABLE - VERSION_VAR FLEX_VERSION) - -# FindFLEX.cmake ends here diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 10e5656..1fe0873 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,6 @@ endif(WITH_METHOD_AES128_GCM) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${FASTD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CAP_INCLUDE_DIR} ${UECC_INCLUDE_DIRS} ${NACL_INCLUDE_DIR}) link_directories(${UECC_LIBRARY_DIRS}) -FLEX_TARGET(fastd_config_lex config.l ${CMAKE_CURRENT_BINARY_DIR}/config.ll.c) BISON_TARGET(fastd_config_parse config.y ${CMAKE_CURRENT_BINARY_DIR}/config.yy.c) add_executable(fastd @@ -21,6 +20,7 @@ add_executable(fastd crypto.c crypto_linux.c handshake.c + lex.c options.c peer.c printf.c @@ -34,7 +34,6 @@ add_executable(fastd task.c tuntap.c protocol_ec25519_fhmqvc.c - ${FLEX_fastd_config_lex_OUTPUTS} ${BISON_fastd_config_parse_OUTPUTS} ${METHODS} ) diff --git a/src/config.c b/src/config.c index 128b114..8a417e9 100644 --- a/src/config.c +++ b/src/config.c @@ -25,8 +25,8 @@ #include "fastd.h" +#include "lex.h" #include "peer.h" -#include #include #include @@ -414,11 +414,10 @@ bool fastd_read_config(fastd_context_t *ctx, fastd_config_t *conf, const char *f char *filename2 = NULL; char *dir = NULL; FILE *file; - yyscan_t scanner; + fastd_lex_t *lex = NULL; fastd_config_pstate *ps; fastd_string_stack_t *strings = NULL; - fastd_config_yylex_init(&scanner); ps = fastd_config_pstate_new(); if (!filename) { @@ -433,7 +432,7 @@ bool fastd_read_config(fastd_context_t *ctx, fastd_config_t *conf, const char *f } } - fastd_config_yyset_in(file, scanner); + lex = fastd_lex_init(file); if (filename) { filename2 = strdup(filename); @@ -458,7 +457,7 @@ bool fastd_read_config(fastd_context_t *ctx, fastd_config_t *conf, const char *f int parse_ret = fastd_config_push_parse(ps, token, &token_val, &loc, ctx, conf, filename, depth+1); while(parse_ret == YYPUSH_MORE) { - token = fastd_config_yylex(&token_val, &loc, scanner); + token = fastd_lex(&token_val, &loc, lex); if (token < 0) { pr_error(ctx, "config error: %s at %s:%i:%i", token_val.error, filename, loc.first_line, loc.first_column); @@ -480,8 +479,8 @@ bool fastd_read_config(fastd_context_t *ctx, fastd_config_t *conf, const char *f end_free: fastd_string_stack_free(strings); + fastd_lex_destroy(lex); fastd_config_pstate_delete(ps); - fastd_config_yylex_destroy(scanner); if(chdir(oldcwd)) pr_error(ctx, "can't chdir to `%s': %s", oldcwd, strerror(errno)); diff --git a/src/config.l b/src/config.l deleted file mode 100644 index c460871..0000000 --- a/src/config.l +++ /dev/null @@ -1,244 +0,0 @@ -/* - Copyright (c) 2012-2013, Matthias Schiffer - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - 2. 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. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. -*/ - - -%option prefix="fastd_config_yy" -%option noyywrap -%option nounput -%option noinput -%option bison-bridge -%option bison-locations -%option reentrant -%option warn - - -%s NEEDSPACE -%s STRING -%s COMMENT - - -%top { -#include -} - -%{ -typedef struct keyword { - const char *keyword; - int token; -} keyword_t; - -/* the keyword list must be sorted */ -static const keyword_t keywords[] = { - { "addresses", TOK_ADDRESSES }, - { "any", TOK_ANY }, - { "as", TOK_AS }, - { "auto", TOK_AUTO }, - { "bind", TOK_BIND }, - { "capabilities", TOK_CAPABILITIES }, - { "crypto", TOK_CRYPTO }, - { "debug", TOK_DEBUG }, - { "default", TOK_DEFAULT }, - { "disestablish", TOK_DISESTABLISH }, - { "down", TOK_DOWN }, - { "drop", TOK_DROP }, - { "early", TOK_EARLY }, - { "error", TOK_ERROR }, - { "establish", TOK_ESTABLISH }, - { "fatal", TOK_FATAL }, - { "float", TOK_FLOAT }, - { "forward", TOK_FORWARD }, - { "from", TOK_FROM }, - { "group", TOK_GROUP }, - { "hide", TOK_HIDE }, - { "include", TOK_INCLUDE }, - { "info", TOK_INFO }, - { "interface", TOK_INTERFACE }, - { "ip", TOK_IP }, - { "ipv4", TOK_IPV4 }, - { "ipv6", TOK_IPV6 }, - { "key", TOK_KEY }, - { "level", TOK_LEVEL }, - { "limit", TOK_LIMIT }, - { "log", TOK_LOG }, - { "mac", TOK_MAC }, - { "method", TOK_METHOD }, - { "mode", TOK_MODE }, - { "mtu", TOK_MTU }, - { "no", TOK_NO }, - { "on", TOK_ON }, - { "peer", TOK_PEER }, - { "peers", TOK_PEERS }, - { "pmtu", TOK_PMTU }, - { "port", TOK_PORT }, - { "post-down", TOK_POST_DOWN }, - { "pre-up", TOK_PRE_UP }, - { "protocol", TOK_PROTOCOL }, - { "remote", TOK_REMOTE }, - { "secret", TOK_SECRET }, - { "stderr", TOK_STDERR }, - { "syslog", TOK_SYSLOG }, - { "tap", TOK_TAP }, - { "to", TOK_TO }, - { "tun", TOK_TUN }, - { "up", TOK_UP }, - { "use", TOK_USE }, - { "user", TOK_USER }, - { "verbose", TOK_VERBOSE }, - { "verify", TOK_VERIFY }, - { "warn", TOK_WARN }, - { "yes", TOK_YES }, -}; - -static int compare_keywords(const void *v1, const void *v2) { - const keyword_t *k1 = v1, *k2 = v2; - return strcmp(k1->keyword, k2->keyword); -} - -#define UPDATE_LOCATION do { \ - yylloc->first_line = yylloc->last_line; \ - yylloc->first_column = yylloc->last_column+1; \ - yylloc->last_column += yyleng; \ - } while (0) -%} - -%% - -{ -[a-z][-a-z0-9]* { - const keyword_t key = {yytext}; - const keyword_t *ret = bsearch(&key, keywords, sizeof(keywords)/sizeof(keyword_t), sizeof(keyword_t), compare_keywords); - - UPDATE_LOCATION; - - if (!ret) { - yylval->error = "syntax error"; - return -1; - } - - BEGIN(NEEDSPACE); - return ret->token; -} - -[0-9]+ { - char *endptr; - - UPDATE_LOCATION; - - yylval->uint64 = strtoull(yytext, &endptr, 10); - if (*endptr) { - yylval->error = "invalid integer constant"; - return -1; - } - - - BEGIN(NEEDSPACE); - return TOK_UINT; -} - -[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} { - UPDATE_LOCATION; - - if (!inet_pton(AF_INET, yytext, &yylval->addr4)) { - yylval->error = "invalid address"; - return -1; - } - - BEGIN(NEEDSPACE); - return TOK_ADDR4; - } - -\[[0-9a-fA-F:]+\] { - UPDATE_LOCATION; - - yytext[yyleng-1] = 0; - - if (!inet_pton(AF_INET6, yytext+1, &yylval->addr6)) { - yylval->error = "invalid address"; - return -1; - } - - BEGIN(NEEDSPACE); - return TOK_ADDR6; - } -} - -{ -[;:\{\}] { UPDATE_LOCATION; BEGIN(INITIAL); return yytext[0]; } - -\n { yylloc->last_column = 0; yylloc->last_line++; BEGIN(INITIAL); } - -[ \t\r] | -#.* | -\/\/.* { UPDATE_LOCATION; BEGIN(INITIAL); } - -\/\* { UPDATE_LOCATION; BEGIN(COMMENT); } -} - -\" { UPDATE_LOCATION; BEGIN(STRING); } -[^"\\\n] { yylloc->last_column++; yymore(); } -\n { yylloc->last_line++; yylloc->last_column = 0; yymore(); } -\\. { yylloc->last_column+=2; yymore(); } -\\\n { yylloc->last_line++; yylloc->last_column = 0; yymore(); } -\" { - int i, esc = 0; - - for (i = 0; i < yyleng; i++) { - if (yytext[i] == '\\') { - i++; - if (yytext[i] == '\n') { - esc+=2; - } - else { - yytext[i-esc-1] = yytext[i]; - esc++; - } - } - else if(esc) { - yytext[i-esc] = yytext[i]; - } - } - yytext[yyleng-esc-1] = 0; - yylval->str = fastd_string_stack_dup(yytext); - BEGIN(NEEDSPACE); - yylloc->last_column++; - return TOK_STRING; - - } - -\*\/ { yylloc->last_column += yyleng; BEGIN(INITIAL); } -[^\n] { yylloc->last_column++; } -\n { yylloc->last_line++; yylloc->last_column = 0; } - -. { - yylloc->first_line = yylloc->last_line; - yylloc->first_column = yylloc->last_column+1; - yylval->error = "syntax error"; - return -1; - } - -<> { return 0; } -<> { yylval->error = "unterminated block comment"; return -1; } -<> { yylval->error = "unterminated string"; return -1; } -%% diff --git a/src/lex.c b/src/lex.c new file mode 100644 index 0000000..575d83e --- /dev/null +++ b/src/lex.c @@ -0,0 +1,457 @@ +/* + Copyright (c) 2012-2013, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. +*/ + + +#include "lex.h" + +#include + + +struct fastd_lex { + FILE *file; + + bool needspace; + + size_t start; + size_t end; + size_t tok_len; + char buffer[1024]; +}; + + +typedef struct keyword { + const char *keyword; + int token; +} keyword_t; + +/* the keyword list must be sorted */ +static const keyword_t keywords[] = { + { "addresses", TOK_ADDRESSES }, + { "any", TOK_ANY }, + { "as", TOK_AS }, + { "auto", TOK_AUTO }, + { "bind", TOK_BIND }, + { "capabilities", TOK_CAPABILITIES }, + { "crypto", TOK_CRYPTO }, + { "debug", TOK_DEBUG }, + { "default", TOK_DEFAULT }, + { "disestablish", TOK_DISESTABLISH }, + { "down", TOK_DOWN }, + { "drop", TOK_DROP }, + { "early", TOK_EARLY }, + { "error", TOK_ERROR }, + { "establish", TOK_ESTABLISH }, + { "fatal", TOK_FATAL }, + { "float", TOK_FLOAT }, + { "forward", TOK_FORWARD }, + { "from", TOK_FROM }, + { "group", TOK_GROUP }, + { "hide", TOK_HIDE }, + { "include", TOK_INCLUDE }, + { "info", TOK_INFO }, + { "interface", TOK_INTERFACE }, + { "ip", TOK_IP }, + { "ipv4", TOK_IPV4 }, + { "ipv6", TOK_IPV6 }, + { "key", TOK_KEY }, + { "level", TOK_LEVEL }, + { "limit", TOK_LIMIT }, + { "log", TOK_LOG }, + { "mac", TOK_MAC }, + { "method", TOK_METHOD }, + { "mode", TOK_MODE }, + { "mtu", TOK_MTU }, + { "no", TOK_NO }, + { "on", TOK_ON }, + { "peer", TOK_PEER }, + { "peers", TOK_PEERS }, + { "pmtu", TOK_PMTU }, + { "port", TOK_PORT }, + { "post-down", TOK_POST_DOWN }, + { "pre-up", TOK_PRE_UP }, + { "protocol", TOK_PROTOCOL }, + { "remote", TOK_REMOTE }, + { "secret", TOK_SECRET }, + { "stderr", TOK_STDERR }, + { "syslog", TOK_SYSLOG }, + { "tap", TOK_TAP }, + { "to", TOK_TO }, + { "tun", TOK_TUN }, + { "up", TOK_UP }, + { "use", TOK_USE }, + { "user", TOK_USER }, + { "verbose", TOK_VERBOSE }, + { "verify", TOK_VERIFY }, + { "warn", TOK_WARN }, + { "yes", TOK_YES }, +}; + +static int compare_keywords(const void *v1, const void *v2) { + const keyword_t *k1 = v1, *k2 = v2; + return strcmp(k1->keyword, k2->keyword); +} + + +static bool advance(fastd_lex_t *lex) { + if (lex->start > 0) { + memmove(lex->buffer, lex->buffer+lex->start, lex->end - lex->start); + lex->end -= lex->start; + lex->start = 0; + } + + if (lex->end == sizeof(lex->buffer)) + return false; + + size_t l = fread(lex->buffer+lex->end, 1, sizeof(lex->buffer) - lex->end, lex->file); + + lex->end += l; + return l; +} + +static inline char current(fastd_lex_t *lex) { + return lex->buffer[lex->start + lex->tok_len]; +} + +static char* get_token(fastd_lex_t *lex) { + return strndup(lex->buffer+lex->start, lex->tok_len); +} + +static bool next(YYLTYPE *yylloc, fastd_lex_t *lex, bool move) { + if (lex->start + lex->tok_len >= lex->end) + return false; + + if (current(lex) == '\n') { + yylloc->last_column = 0; + yylloc->last_line++; + } + else { + yylloc->last_column++; + } + + if (move) + lex->start++; + else + lex->tok_len++; + + + if (lex->start + lex->tok_len >= lex->end) + return advance(lex); + + return true; +} + +static void consume(fastd_lex_t *lex, bool needspace) { + lex->start += lex->tok_len; + lex->tok_len = 0; + + lex->needspace = needspace; +} + +static int syntax_error(YYSTYPE *yylval, fastd_lex_t *lex) { + yylval->error = "syntax error"; + return -1; +} + +static int io_error(YYSTYPE *yylval, fastd_lex_t *lex) { + yylval->error = "I/O error"; + return -1; +} + +static inline int end(YYSTYPE *yylval, fastd_lex_t *lex) { + if (ferror(lex->file)) + return io_error(yylval, lex); + + return 0; +} + +static int consume_comment(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex) { + char prev = 0; + + while (next(yylloc, lex, true)) { + if (prev == '*' && current(lex) == '/') { + next(yylloc, lex, true); + consume(lex, false); + return 0; + } + + prev = current(lex); + } + + if (ferror(lex->file)) + return io_error(yylval, lex); + + yylval->error = "unterminated block comment"; + return -1; +} + +static int unterminated_string(YYSTYPE *yylval, fastd_lex_t *lex) { + if (ferror(lex->file)) + return io_error(yylval, lex); + + yylval->error = "unterminated string"; + return -1; +} + +static int parse_string(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex) { + char *buf = NULL; + size_t len = 1024; + size_t pos = 0; + + if (lex->needspace) + return syntax_error(yylval, lex); + + buf = malloc(len); + + while (true) { + if (!next(yylloc, lex, true)) { + free(buf); + return unterminated_string(yylval, lex); + } + + char cur = current(lex); + + if (cur == '"') + break; + + if (cur == '\\') { + if (!next(yylloc, lex, true)) { + free(buf); + return unterminated_string(yylval, lex); + } + + cur = current(lex); + + if (cur == '\n') + continue; + } + + if (pos >= len) { + len *= 2; + buf = realloc(buf, len); + } + + buf[pos++] = cur; + } + + yylval->str = fastd_string_stack_dupn(buf, pos); + free(buf); + + next(yylloc, lex, true); + consume(lex, true); + + return TOK_STRING; +} + +static int parse_ipv6_address(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex) { + if (lex->needspace) + return syntax_error(yylval, lex); + + while (next(yylloc, lex, false)) { + char cur = current(lex); + + if (!((cur >= '0' && cur <= '9') || cur == ':')) + break; + } + + bool ok = (current(lex) == ']'); + + if (ok) { + lex->buffer[lex->start + lex->tok_len] = 0; + ok = inet_pton(AF_INET6, lex->buffer+lex->start+1, &yylval->addr6); + } + + if (!ok) { + yylval->error = "invalid address"; + return -1; + } + + next(yylloc, lex, true); + consume(lex, true); + + return TOK_ADDR6; +} + +static int parse_ipv4_address(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex) { + if (lex->needspace) + return syntax_error(yylval, lex); + + while (next(yylloc, lex, false)) { + char cur = current(lex); + + if (!((cur >= '0' && cur <= '9') || cur == '.')) + break; + } + + char *token = get_token(lex); + bool ok = inet_pton(AF_INET, token, &yylval->addr4); + + free(token); + + if (!ok) { + yylval->error = "invalid address"; + return -1; + } + + return TOK_ADDR4; +} + +static int parse_number(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex) { + if (lex->needspace) + return syntax_error(yylval, lex); + + while (next(yylloc, lex, false)) { + char cur = current(lex); + + if (cur == '.') + return parse_ipv4_address(yylval, yylloc, lex); + + if (!(cur >= '0' && cur <= '9')) + break; + } + + char *endptr, *token = get_token(lex); + yylval->uint64 = strtoull(token, &endptr, 10); + + bool ok = !*endptr; + free(token); + + if (!ok) { + yylval->error = "invalid integer constant"; + return -1; + } + + return TOK_UINT; +} + +static int parse_keyword(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex) { + if (lex->needspace) + return syntax_error(yylval, lex); + + while (next(yylloc, lex, false)) { + char cur = current(lex); + + if (!((cur >= 'a' && cur <= 'z') || (cur >= '0' && cur <= '9') || cur == '-')) + break; + } + + char *token = get_token(lex); + const keyword_t key = {token}; + const keyword_t *ret = bsearch(&key, keywords, sizeof(keywords)/sizeof(keyword_t), sizeof(keyword_t), compare_keywords); + free(token); + + if (!ret) + return syntax_error(yylval, lex); + + consume(lex, true); + + return ret->token; +} + +fastd_lex_t* fastd_lex_init(FILE *file) { + fastd_lex_t *lex = calloc(1, sizeof(fastd_lex_t)); + lex->file = file; + + advance(lex); + + return lex; +} + +void fastd_lex_destroy(fastd_lex_t *lex) { + if (!lex) + return; + + free(lex); +} + +int fastd_lex(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex) { + int token; + + while (lex->end > lex->start) { + yylloc->first_line = yylloc->last_line; + yylloc->first_column = yylloc->last_column+1; + + switch (current(lex)) { + case ' ': + case '\n': + case '\t': + case '\r': + next(yylloc, lex, true); + consume(lex, false); + continue; + + case ';': + case ':': + case '{': + case '}': + token = current(lex); + next(yylloc, lex, true); + consume(lex, false); + return token; + + case '/': + if (!next(yylloc, lex, true)) + return syntax_error(yylval, lex); + + if (current(lex) == '*') { + token = consume_comment(yylval, yylloc, lex); + if (token) + return token; + + continue; + } + + if (current(lex) != '/') + return syntax_error(yylval, lex); + + /* fall-through */ + case '#': + while (next(yylloc, lex, true)) { + if (current(lex) == '\n') + break; + } + + next(yylloc, lex, true); + consume(lex, false); + continue; + + case '"': + return parse_string(yylval, yylloc, lex); + + case '[': + return parse_ipv6_address(yylval, yylloc, lex); + + case '0' ... '9': + return parse_number(yylval, yylloc, lex); + + case 'a' ... 'z': + return parse_keyword(yylval, yylloc, lex); + + default: + return syntax_error(yylval, lex); + } + } + + return end(yylval, lex); +} diff --git a/src/lex.h b/src/lex.h new file mode 100644 index 0000000..2a78421 --- /dev/null +++ b/src/lex.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2012-2013, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 _FASTD_LEX_H_ +#define _FASTD_LEX_H_ + +#include "types.h" +#include + +#include + + +fastd_lex_t* fastd_lex_init(FILE *file); +void fastd_lex_destroy(fastd_lex_t *lex); + +int fastd_lex(YYSTYPE *yylval, YYLTYPE *yylloc, fastd_lex_t *lex); + +#endif /* _FASTD_LEX_H_ */ diff --git a/src/types.h b/src/types.h index b1026b5..8eaa55d 100644 --- a/src/types.h +++ b/src/types.h @@ -87,6 +87,7 @@ typedef struct fastd_method fastd_method_t; typedef struct fastd_handshake fastd_handshake_t; +typedef struct fastd_lex fastd_lex_t; typedef struct fastd_string_stack fastd_string_stack_t; typedef struct fastd_resolve_return fastd_resolve_return_t; -- cgit v1.2.3