diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dddf83..ac156f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,9 @@ endif() if (NOT HAVE_SYS_TREE) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/compat/sys/tree.h.in ${CMAKE_CURRENT_BINARY_DIR}/compat/sys/tree.h) + + include_directories(${PROJECT_BINARY_DIR}/compat) + endif(NOT HAVE_SYS_TREE) if (NOT HAVE_SYS_QUEUE) @@ -303,7 +306,6 @@ if (EVHTP_BUILD_SHARED) set_target_properties(evhtp PROPERTIES SOVERSION "${PROJECT_VERSION}") endif() -add_subdirectory(tools) add_subdirectory(examples) if (NOT LIB_INSTALL_DIR) diff --git a/ChangeLog b/ChangeLog index 0a9de56..43be2b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,48 @@ +v1.2.12-pre2 + o Internalize some structs / deprecate evhtp_set_hook() (50ab327 Nathan French) + o remove cruft (1b1a037 Nathan French) + o add include directory for compat/sys headers (948c547 Nathan French) + o export flag functions (3467cbb Nathan French) + o formatting (4ec8dd3 Nathan French) + +v1.2.12-pre1 + o (evhtp_send_reply): Grab reference to bufferevent during write. (a976a2f Marcus Sundberg) + o add thread exit callback for cleaning (0c7d9c4 jgli) + o fix memory leak (a6b00cc jgli) + o fix thread exit callback type (c8978b6 jgli) + o Updates for threading functionality. (b634002 Mark Ellzey) + o Added evhtp_accept_socket (a497a14 Mark Ellzey) + o Forgot to export evhtp_accept_socke. (c94cb5b Mark Ellzey) + o Formatting. (4a78297 Mark Ellzey) + o Maybe I should spell rite. (2114210 Mark Ellzey) + o Fix cmake compilation issue in centos7 (dfc8c2b kaustubh-d) + o Remove double-free when SSL is used. (ee32b2a Jacob Marble) + o Add CPack commands to build a debian package. (0c4a8ec Tom Pusateri) + o [docs] added some doxygen groups (8a247f1 Mark Ellzey) + o check res for bufferevent_socket_connect (#136) (70b68d4 mthomas) + o Regression from commit 67ed0bc (c96c51e Ultima1252) + o Added build/* to gitignore (c64f1dc Mark Ellzey) + o Updating license to include Marcus Sundberg (801c52f Mark Ellzey) + o [htparse] fix up some stuff to make coverity happy (abc7eb4 Mark Ellzey) + o More coverity fixes (7d3cc52 Mark Ellzey) + o check for sockopt returns (534bb48 Mark Ellzey) + o master travis updates (2c6bb88 Mark Ellzey) + o added testbigendian module for old cmake (fb6a866 Mark Ellzey) + o Added initial evhtp_json API (6e48770 Mark Ellzey) + o LICENSE update for ripping liblz json api (fb473ef Mark Ellzey) + o Removing the SIGNED.md file (outdated anyway) (d4bcfa8 Mark Ellzey) + o Update README (6ef073e Mark Ellzey) + o updates / formatting / renames (926e355 Nathan French) + o static funcs from now on will just return int (5112b6d Nathan French) + o fix htp__use_threads_ call error (2ed2f7f weijiazhen) + o FIX : Socket leakage on error #6 (d13b72b Nathan French) + o Issue#6: make evhtp_accept_socket conform to api (d0347dc Nathan French) + o Establish conformity through flags. (58da6dd Nathan French) + o request flags (71341d9 Nathan French) + o EVHTP_CONN flags (087e9a7 Nathan French) + o Flag ops (and related functions) / cleanup (0abc96f Nathan French) + o Add flags accessor (e0f04aa Nathan French) + v1.2.11 o Grab reference to bufferevent during write. (a976a2f Marcus Sundberg) o add thread exit callback for cleaning (0c7d9c4 jgli) diff --git a/evhtp.c b/evhtp.c index 34b5d47..37a3cd0 100644 --- a/evhtp.c +++ b/evhtp.c @@ -26,6 +26,40 @@ #include "evhtp_numtoa.h" #include "evhtp.h" +/** + * @brief structure containing a single callback and configuration + * + * The definition structure which is used within the evhtp_callbacks_t + * structure. This holds information about what should execute for either + * a single or regex path. + * + * For example, if you registered a callback to be executed on a request + * for "/herp/derp", your defined callback will be executed. + * + * Optionally you can set callback-specific hooks just like per-connection + * hooks using the same rules. + * + */ +struct evhtp_callback_s { + evhtp_callback_type type; /**< the type of callback (regex|path) */ + evhtp_callback_cb cb; /**< the actual callback function */ + void * cbarg; /**< user-defind arguments passed to the cb */ + evhtp_hooks_t * hooks; /**< per-callback hooks */ + size_t len; + + union { + char * path; + char * glob; +#ifndef EVHTP_DISABLE_REGEX + regex_t * regex; +#endif + } val; + + TAILQ_ENTRY(evhtp_callback_s) next; +}; + +TAILQ_HEAD(evhtp_callbacks_s, evhtp_callback_s); + #ifdef EVHTP_DEBUG static void @@ -262,26 +296,6 @@ strndup(const char * s, size_t n) * PRIVATE FUNCTIONS */ -/** - * @brief a weak hash function - * - * @param str a null terminated string - * - * @return an unsigned integer hash of str - */ -static inline unsigned int -htp__quick_hash_(const char * str) -{ - unsigned int h = 0; - - for (; *str; str++) - { - h = 31 * h + *str; - } - - return h; -} - /** * * @brief helper macro to determine if http version is HTTP/1.0 @@ -554,8 +568,8 @@ htp__hook_connection_write_(evhtp_connection_t * connection) * @return */ static int -htp__glob_match2_(const char * pattern, size_t plen, - const char * string, size_t str_len) +htp__glob_match_(const char * pattern, size_t plen, + const char * string, size_t str_len) { while (plen) { @@ -574,8 +588,8 @@ htp__glob_match2_(const char * pattern, size_t plen, while (str_len) { - if (htp__glob_match2_(pattern + 1, plen - 1, - string, str_len)) + if (htp__glob_match_(pattern + 1, plen - 1, + string, str_len)) { return 1; /* match */ } @@ -617,27 +631,6 @@ htp__glob_match2_(const char * pattern, size_t plen, } return 0; -} /* htp__glob_match2_ */ - -static inline int -htp__glob_match_(const char * pattern, size_t pat_len, const char * string, size_t str_len) -{ - if (evhtp_unlikely(!pattern || !string)) - { - return 0; - } - - if (pat_len == 0) - { - pat_len = strlen(pattern); - } - - if (str_len == 0) - { - str_len = strlen(string); - } - - return htp__glob_match2_(pattern, pat_len, string, str_len); } /* htp__glob_match_ */ static evhtp_callback_t * @@ -660,6 +653,11 @@ htp__callback_find_(evhtp_callbacks_t * cbs, { switch (callback->type) { case evhtp_callback_type_hash: + if (callback->val.path[1] != path[1]) + { + continue; + } + if (strcmp(callback->val.path, path) == 0) { *start_offset = 0; @@ -1224,7 +1222,7 @@ htp__request_find_vhost_(evhtp_t * evhtp, const char * name) continue; } - if (htp__glob_match_(evhtp_vhost->server_name, 0, name, 0) == 1) + if (htp__glob_match_(evhtp_vhost->server_name, strlen(evhtp_vhost->server_name), name, strlen(name)) == 1) { return evhtp_vhost; } @@ -1236,7 +1234,7 @@ htp__request_find_vhost_(evhtp_t * evhtp, const char * name) continue; } - if (htp__glob_match_(evhtp_alias->alias, 0, name, 0) == 1) + if (htp__glob_match_(evhtp_alias->alias, strlen(evhtp_alias->alias), name, strlen(name)) == 1) { return evhtp_vhost; } @@ -3779,7 +3777,6 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c switch (type) { case evhtp_callback_type_hash: - hcb->hash = htp__quick_hash_(path); hcb->val.path = strdup(path); break; #ifndef EVHTP_DISABLE_REGEX @@ -3848,8 +3845,8 @@ evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) return 0; } -int -evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg) +static int +htp__set_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg) { if (*hooks == NULL) { @@ -3925,7 +3922,31 @@ evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void } /* switch */ return 0; -} /* evhtp_set_hook */ +} /* htp__set_hook_ */ + +int +evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg) +{ + return htp__set_hook_(hooks, type, cb, arg); +} + +int +evhtp_callback_set_hook(evhtp_callback_t * callback, evhtp_hook_type type, evhtp_hook cb, void * arg) +{ + return htp__set_hook_(&callback->hooks, type, cb, arg); +} + +int +evhtp_request_set_hook(evhtp_request_t * req, evhtp_hook_type type, evhtp_hook cb, void * arg) +{ + return htp__set_hook_(&req->hooks, type, cb, arg); +} + +int +evhtp_connection_set_hook(evhtp_connection_t * conn, evhtp_hook_type type, evhtp_hook cb, void * arg) +{ + return htp__set_hook_(&conn->hooks, type, cb, arg); +} int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type) @@ -4016,6 +4037,34 @@ evhtp_unset_all_hooks(evhtp_hooks_t ** hooks) return res; } /* evhtp_unset_all_hooks */ +evhtp_hooks_t * +evhtp_connection_get_hooks(evhtp_connection_t * c) +{ + if (evhtp_unlikely(c == NULL)) + { + return NULL; + } + + return c->hooks; +} + +evhtp_hooks_t * +evhtp_request_get_hooks(evhtp_request_t * r) +{ + if (evhtp_unlikely(r == NULL)) + { + return NULL; + } + + return r->hooks; +} + +evhtp_hooks_t * +evhtp_callback_get_hooks(evhtp_callback_t * cb) +{ + return cb->hooks; +} + evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg) { @@ -4690,6 +4739,7 @@ evhtp_set_parser_flags(evhtp_t * htp, int flags) { \ return v->flags; \ } \ + return -1; \ } HTP_FLAG_FNGEN(, evhtp_t *); diff --git a/evhtp.h b/evhtp.h index e591ac1..7bae4e2 100644 --- a/evhtp.h +++ b/evhtp.h @@ -51,6 +51,8 @@ extern "C" { } while (0) #endif +struct evhtp_callback_s; +struct evhtp_callbacks_s; #ifndef EVHTP_DISABLE_SSL typedef SSL_SESSION evhtp_ssl_sess_t; @@ -337,39 +339,6 @@ struct evhtp_s { TAILQ_ENTRY(evhtp_s) next_vhost; }; -/** - * @brief structure containing a single callback and configuration - * - * The definition structure which is used within the evhtp_callbacks_t - * structure. This holds information about what should execute for either - * a single or regex path. - * - * For example, if you registered a callback to be executed on a request - * for "/herp/derp", your defined callback will be executed. - * - * Optionally you can set callback-specific hooks just like per-connection - * hooks using the same rules. - * - */ -struct evhtp_callback_s { - evhtp_callback_type type; /**< the type of callback (regex|path) */ - evhtp_callback_cb cb; /**< the actual callback function */ - unsigned int hash; /**< the full hash generated integer */ - void * cbarg; /**< user-defind arguments passed to the cb */ - evhtp_hooks_t * hooks; /**< per-callback hooks */ - - union { - char * path; - char * glob; -#ifndef EVHTP_DISABLE_REGEX - regex_t * regex; -#endif - } val; - - TAILQ_ENTRY(evhtp_callback_s) next; -}; - -TAILQ_HEAD(evhtp_callbacks_s, evhtp_callback_s); /** * @brief a generic key/value structure @@ -577,6 +546,15 @@ struct evhtp_ssl_cfg_s { */ EVHTP_EXPORT evhtp_t * evhtp_new(evbase_t * evbase, void * arg); +EVHTP_EXPORT void evhtp_enable_flag(evhtp_t *, int); +EVHTP_EXPORT void evhtp_connection_enable_flag(evhtp_connection_t *, int); +EVHTP_EXPORT void evhtp_request_enable_flag(evhtp_request_t *, int); +EVHTP_EXPORT int evhtp_get_flags(evhtp_t *); +EVHTP_EXPORT int evhtp_connection_get_flags(evhtp_connection_t *); +EVHTP_EXPORT int evhtp_request_get_flags(evhtp_request_t *); +EVHTP_EXPORT void evhtp_disable_flag(evhtp_t *, int); +EVHTP_EXPORT void evhtp_connection_disable_flag(evhtp_connection_t *, int); +EVHTP_EXPORT void evhtp_request_disable_flag(evhtp_request_t *, int); /** * @brief free a evhtp_t context @@ -796,9 +774,12 @@ EVHTP_EXPORT evhtp_callback_t * evhtp_get_cb(evhtp_t * htp, const char * needle) * * @return 0 on success, -1 on error (if hooks is NULL, it is allocated) */ -EVHTP_EXPORT int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, - evhtp_hook cb, void * arg); +EVHTP_EXPORT int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg); +DEPRECATED("use evhtp_[connection|request|callback]_set_hook() instead of set_hook directly"); +EVHTP_EXPORT int evhtp_connection_set_hook(evhtp_connection_t * c, evhtp_hook_type type, evhtp_hook cb, void * arg); +EVHTP_EXPORT int evhtp_request_set_hook(evhtp_request_t * r, evhtp_hook_type type, evhtp_hook cb, void * arg); +EVHTP_EXPORT int evhtp_callback_set_hook(evhtp_callback_t * cb, evhtp_hook_type type, evhtp_hook hookcb, void * arg); /** * @brief remove a specific hook from being called. diff --git a/evhtp_json.c b/evhtp_json.c index dd2440f..3a65f29 100644 --- a/evhtp_json.c +++ b/evhtp_json.c @@ -10,7 +10,6 @@ #include #include -#include "internal.h" #include "evhtp_heap.h" #include "evhtp_json.h" diff --git a/examples/test.c b/examples/test.c index d9d481a..9eacad7 100644 --- a/examples/test.c +++ b/examples/test.c @@ -97,9 +97,15 @@ pause_init_cb(evhtp_request_t * req, evhtp_path_t * path, void * arg) { pause->timer_ev = evtimer_new(evbase, resume_request_timer, pause); pause->request = req; - evhtp_set_hook(&req->hooks, evhtp_hook_on_header, pause_cb, pause); - evhtp_set_hook(&req->hooks, evhtp_hook_on_request_fini, pause_request_fini, pause); - evhtp_set_hook(&req->conn->hooks, evhtp_hook_on_connection_fini, pause_connection_fini, NULL); + evhtp_request_set_hook(req, + evhtp_hook_on_header, pause_cb, pause); + + evhtp_request_set_hook(req, + evhtp_hook_on_request_fini, pause_request_fini, pause); + + evhtp_connection_set_hook(req->conn, + evhtp_hook_on_connection_fini, + pause_connection_fini, NULL); return EVHTP_RES_OK; } @@ -377,14 +383,13 @@ set_my_connection_handlers(evhtp_connection_t * conn, void * arg) { struct timeval tick; struct ev_token_bucket_cfg * tcfg = NULL; - evhtp_set_hook(&conn->hooks, evhtp_hook_on_header, print_kv, "foo"); - evhtp_set_hook(&conn->hooks, evhtp_hook_on_headers, print_kvs, "bar"); - evhtp_set_hook(&conn->hooks, evhtp_hook_on_path, print_path, "baz"); - evhtp_set_hook(&conn->hooks, evhtp_hook_on_read, print_data, "derp"); - evhtp_set_hook(&conn->hooks, evhtp_hook_on_new_chunk, print_new_chunk_len, NULL); - evhtp_set_hook(&conn->hooks, evhtp_hook_on_chunk_complete, print_chunk_complete, NULL); - evhtp_set_hook(&conn->hooks, evhtp_hook_on_chunks_complete, print_chunks_complete, NULL); - /* evhtp_set_hook(&conn->hooks, evhtp_hook_on_hostname, print_hostname, NULL); */ + evhtp_connection_set_hook(conn, evhtp_hook_on_header, print_kv, "foo"); + evhtp_connection_set_hook(conn, evhtp_hook_on_headers, print_kvs, "bar"); + evhtp_connection_set_hook(conn, evhtp_hook_on_path, print_path, "baz"); + evhtp_connection_set_hook(conn, evhtp_hook_on_read, print_data, "derp"); + evhtp_connection_set_hook(conn, evhtp_hook_on_new_chunk, print_new_chunk_len, NULL); + evhtp_connection_set_hook(conn, evhtp_hook_on_chunk_complete, print_chunk_complete, NULL); + evhtp_connection_set_hook(conn, evhtp_hook_on_chunks_complete, print_chunks_complete, NULL); if (bw_limit > 0) { tick.tv_sec = 0; @@ -395,7 +400,7 @@ set_my_connection_handlers(evhtp_connection_t * conn, void * arg) { bufferevent_set_rate_limit(conn->bev, tcfg); } - evhtp_set_hook(&conn->hooks, evhtp_hook_on_request_fini, test_fini, tcfg); + evhtp_connection_set_hook(conn, evhtp_hook_on_request_fini, test_fini, tcfg); return EVHTP_RES_OK; } @@ -585,14 +590,14 @@ main(int argc, char ** argv) { evhtp_assert(cb_12 != NULL); /* set a callback to pause on each header for cb_7 */ - evhtp_set_hook(&cb_7->hooks, evhtp_hook_on_path, pause_init_cb, NULL); + evhtp_callback_set_hook(cb_7, evhtp_hook_on_path, pause_init_cb, NULL); /* set a callback to set hooks specifically for the cb_6 callback */ #ifndef EVHTP_DISABLE_REGEX - evhtp_set_hook(&cb_6->hooks, evhtp_hook_on_headers, test_regex_hdrs_cb, NULL); + evhtp_callback_set_hook(cb_6, evhtp_hook_on_headers, test_regex_hdrs_cb, NULL); #endif - evhtp_set_hook(&cb_10->hooks, evhtp_hook_on_headers, set_max_body, NULL); + evhtp_callback_set_hook(cb_10, evhtp_hook_on_headers, set_max_body, NULL); /* set a default request handler */ evhtp_set_gencb(htp, test_default_cb, "foobarbaz"); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt deleted file mode 100644 index 25eac1c..0000000 --- a/tools/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_custom_target(tools) - -add_executable(lambda-pp EXCLUDE_FROM_ALL lambda-pp.c) - -add_dependencies(tools lambda-pp) diff --git a/tools/lambda-pp.c b/tools/lambda-pp.c deleted file mode 100644 index 2ef8c25..0000000 --- a/tools/lambda-pp.c +++ /dev/null @@ -1,684 +0,0 @@ -/* -* Copyright (C) 2014 -* Wolfgang Bumiller -* Dale Weiler -* -* Permission is hereby granted, free of charge, to any person obtaining a copy of -* this software and associated documentation files (the "Software"), to deal in -* the Software without restriction, including without limitation the rights to -* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -* of the Software, and to permit persons to whom the Software is furnished to do -* so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ -#include -#include -#include -#include -#include -#include - -#define isalpha(a) ((((unsigned)(a)|32)-'a') < 26) -#define isdigit(a) (((unsigned)(a)-'0') < 10) -#define isalnum(a) (isalpha(a) || isdigit(a)) -#define isspace(a) (((a) >= '\t' && (a) <= '\r') || (a) == ' ') - -static const char *DefaultKeyword = "lambda"; - -typedef struct { - size_t begin; - size_t length; -} lambda_range_t; - -typedef struct { - size_t pos; - size_t line; -} lambda_position_t; - -typedef struct { - const char *file; - char *data; - size_t length; - size_t line; - const char *keyword; - size_t keylength; - bool short_enabled; -} lambda_source_t; - -typedef struct { - size_t start; - lambda_range_t decl; - lambda_range_t body; - size_t name_offset; - size_t decl_line; - size_t body_line; - size_t end_line; - bool is_short; -} lambda_t; - -typedef struct { - union { - char *chars; - lambda_t *funcs; - lambda_position_t *positions; - }; - size_t size; - size_t elements; - size_t length; -} lambda_vector_t; - -typedef struct { - lambda_vector_t lambdas; - lambda_vector_t positions; -} parse_data_t; - -typedef enum { - PARSE_NORMAL, PARSE_TYPE, PARSE_LAMBDA, PARSE_LAMBDA_EXPRESSION -} parse_type_t; - -static size_t parse(lambda_source_t *source, parse_data_t *data, size_t j, parse_type_t parsetype, size_t *nameofs); - -/* Vector */ -static inline bool lambda_vector_init(lambda_vector_t *vec, size_t size) { - vec->length = 32; - vec->size = size; - vec->elements = 0; - return (vec->chars = (char *)malloc(vec->length * vec->size)); -} - -static inline void lambda_vector_destroy(lambda_vector_t *vec) { - free(vec->chars); -} - -static inline bool lambda_vector_resize(lambda_vector_t *vec) { - if (vec->elements != vec->length) - return true; - vec->length <<= 1; - char *temp = realloc(vec->chars, vec->length * vec->size); - if (!temp) - return false; - vec->chars = temp; - return true; -} - -static inline bool lambda_vector_push_char(lambda_vector_t *vec, char ch) { - if (!lambda_vector_resize(vec)) - return false; - vec->chars[vec->elements++] = ch; - return true; -} - -static inline bool lambda_vector_create_lambda(lambda_vector_t *vec, size_t *idx) { - if (!lambda_vector_resize(vec)) - return false; - *idx = vec->elements++; - memset(&vec->funcs[*idx], 0, sizeof(lambda_t)); - return true; -} - -static inline bool lambda_vector_push_position(lambda_vector_t *vec, size_t pos, size_t line) { - if (!lambda_vector_resize(vec)) - return false; - vec->positions[vec->elements].pos = pos; - vec->positions[vec->elements].line = line; - vec->elements++; - return true; -} - -static inline void lambda_source_init(lambda_source_t *source) { - memset(source, 0, sizeof(*source)); - source->keyword = DefaultKeyword; - source->short_enabled = true; -} - -/* Source */ -static void parse_error(lambda_source_t *source, const char *message, ...) { - char buffer[2048]; - va_list va; - va_start(va, message); - vsnprintf(buffer, sizeof(buffer), message, va); - va_end(va); - fprintf(stderr, "%s:%zu error: %s\n", source->file, source->line, buffer); - fflush(stderr); -} - -static bool parse_open(lambda_source_t *source, FILE *handle) { - if (!handle) - return false; - - source->line = 1; - - if (fseek(handle, 0, SEEK_END) != -1) { - source->length = ftell(handle); - fseek(handle, 0, SEEK_SET); - - if (!(source->data = (char *)malloc(source->length))) - goto parse_open_oom; - if (fread(source->data, source->length, 1, handle) != 1) - goto parse_open_failed; - } - else { - static const size_t bs = 4096; - source->length = 0; - if (!(source->data = (char*)malloc(bs))) - goto parse_open_oom; - while (true) { - size_t r = fread(source->data, 1, bs, handle); - source->length += r; - if (feof(handle)) - break; - if (ferror(handle) && errno != EINTR) - goto parse_open_failed; - char *temp = (char*)realloc(source->data, source->length + bs); - if (!temp) - goto parse_open_oom; - source->data = temp; - } - } - - fclose(handle); - return true; - -parse_open_oom: - parse_error(source, "out of memory"); -parse_open_failed: - free(source->data); - fclose(handle); - return false; -} - -static inline void parse_close(lambda_source_t *source) { - free(source->data); -} - -/* Parser */ -static inline size_t parse_skip_string(lambda_source_t *source, size_t i, char check) { - while (i != source->length) { - if (source->data[i] == check) - return i + 1; - else if (source->data[i] == '\\') - if (++i == source->length) - break; - ++i; - } - return i; -} - -static inline size_t parse_skip_white(lambda_source_t *source, size_t i) { - while (i != source->length && isspace(source->data[i])) { - if (source->data[i] == '\n') - source->line++; - ++i; - } - return i; -} - -static size_t parse_word(lambda_source_t *source, parse_data_t *data, size_t j, size_t i) { - if (j != i) { - if (strncmp(source->data + j, source->keyword, source->keylength) == 0) - return parse(source, data, i, PARSE_LAMBDA, false); - } - if (source->data[i] == '\n') - source->line++; - else if (!strncmp(source->data + i, "//", 2)) { - /* Single line comments */ - i = strchr(source->data + i, '\n') - source->data; - } else if (!strncmp(source->data + i, "/*", 2)) { - /* Multi line comments */ - i = strstr(source->data + i, "*/") - source->data; - } - return i; -} - -#define ERROR ((size_t)-1) - -static size_t parse(lambda_source_t *source, parse_data_t *data, size_t i, parse_type_t parsetype, size_t *nameofs) { - lambda_vector_t parens; - size_t lambda = 0; - bool mark = (!parsetype && !nameofs); - size_t protopos = i; - bool protomove = true; - bool preprocessor = false; - bool expectbody = false; - bool movename = false; - /* 'mark' actually means this is the outer most call and we should - * remember where to put prototypes now! - * when protomove is true we move the protopos along whitespace so that - * the lambdas don't get stuck to the tail of hte previous functions. - * Also we need to put lambdas after #include lines so if we encounter - * a preprocessor directive we create another position marker starting - * at the nest new line - */ - - lambda_vector_init(&parens, sizeof(char)); - - - if (parsetype == PARSE_LAMBDA) { - if (!lambda_vector_create_lambda(&data->lambdas, &lambda)) - goto parse_oom; - lambda_t *l = &data->lambdas.funcs[lambda]; - l->start = i - 6; - i = parse_skip_white(source, i); - l->decl.begin = i; - l->decl_line = source->line; - size_t ofs = 0; - if ((i = parse(source, data, i, PARSE_TYPE, &ofs)) == ERROR) - goto parse_error; - l->name_offset = ofs - l->decl.begin; - l->decl.length = i - l->decl.begin; - l->body.begin = i; - l->body_line = source->line; - i = parse_skip_white(source, i); - if (source->short_enabled) { - if (source->data[i] == '=' && source->data[i+1] == '>') { - l->body.begin = i += 2; - l->is_short = true; - parsetype = PARSE_LAMBDA_EXPRESSION; - } - } - } - - size_t j = i; - while (i < source->length) { - if (mark && !parens.elements) { - if (protomove) { - if (isspace(source->data[i])) { - if (source->data[i] == '\n') - source->line++; - protopos = j = ++i; - continue; - } - protomove = false; - if (!lambda_vector_push_position(&data->positions, protopos, source->line)) - goto parse_oom; - } - - if (source->data[i] == ';') { - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - j = ++i; - protomove = true; - protopos = i; - continue; - } - - if (source->data[i] == '#') { - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - j = ++i; - protomove = false; - protopos = i; - preprocessor = true; - continue; - } - if (preprocessor && source->data[i] == '\n') { - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - j = ++i; - protomove = true; - protopos = i; - preprocessor = false; - continue; - } - } - - if (movename) { - if (source->data[i] != '*' && source->data[i] != '(' && !isspace(source->data[i])) - movename = false; - else if (source->data[i] != '(') - *nameofs = i+1; - } - - if (source->data[i] == '"') { - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - j = i = parse_skip_string(source, i+1, source->data[i]); - } else if (source->data[i] == '\'') { - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - j = i = parse_skip_string(source, i+1, source->data[i]); - } else if (strchr("([{", source->data[i])) { - if (nameofs && !parens.elements) { - if (expectbody && source->data[i] == '{') { - lambda_vector_destroy(&parens); - return i; - } - if (!expectbody && source->data[i] == '(') { - expectbody = true; - movename = true; - *nameofs = i; - } - } - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - if (!lambda_vector_push_char(&parens, strchr("([{)]}", source->data[i])[3])) - goto parse_oom; - j = ++i; - } else if (strchr(")]}", source->data[i])) { - if (!parens.elements) { - parse_error(source, "too many closing parenthesis"); - goto parse_error; - } - char back = parens.chars[parens.elements >= 1 ? parens.elements - 1 : 0]; - if (source->data[i] != back) { - parse_error(source, "mismatching `%c' and `%c'", back, source->data[i]); - goto parse_error; - } - if (parens.elements != 0) - parens.elements--; - if (source->data[i] == '}' && !parens.elements) { - if (parsetype == PARSE_LAMBDA) - goto finish_lambda; - else if (nameofs) { - if (!expectbody) - movename = true; - } - } - bool domark = (mark && !parens.elements && source->data[i] == '}'); - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - j = ++i; - if (domark) { - protopos = i; - protomove = true; - } - } else if (source->data[i] != '_' && !isalnum(source->data[i])) { - if (!nameofs && (i = parse_word(source, data, j, i)) == ERROR) - goto parse_error; - if (!parens.elements) { - if (parsetype == PARSE_LAMBDA_EXPRESSION && source->data[i] == ';') - goto finish_lambda; - if (source->short_enabled) { - if (parsetype == PARSE_TYPE && expectbody && source->data[i] == '=' && source->data[i+1] == '>') { - lambda_vector_destroy(&parens); - return i; - } - } - } - j = ++i; - } else - ++i; - } - - lambda_vector_destroy(&parens); - return i; - -parse_oom: - parse_error(source, "out of memory"); -parse_error: - lambda_vector_destroy(&parens); - return ERROR; -finish_lambda: - { - lambda_t *l = &data->lambdas.funcs[lambda]; - l->body.length = i - l->body.begin; - l->end_line = source->line; - lambda_vector_destroy(&parens); - return i; - } -} - -/* Generator */ -static inline void generate_marker(FILE *out, const char *file, size_t line, bool newline) { - fprintf(out, "%s#line %zu \"%s\"\n", newline ? "\n" : "", line, file); -} - -static inline void generate_begin(FILE *out, lambda_source_t *source, lambda_vector_t *lambdas, size_t idx) { - generate_marker(out, source->file, lambdas->funcs[idx].decl_line, true); - fprintf(out, "static "); - size_t ofs = lambdas->funcs[idx].name_offset; - fwrite(source->data + lambdas->funcs[idx].decl.begin, ofs, 1, out); - fprintf(out, " lambda_%zu", idx); - fwrite(source->data + lambdas->funcs[idx].decl.begin+ofs, lambdas->funcs[idx].decl.length-ofs, 1, out); -} - -static size_t next_prototype_position(parse_data_t *data, size_t lam, size_t proto) { - if (lam == data->lambdas.elements) - return data->positions.elements; - for (; proto != data->positions.elements; ++proto) { - if (data->positions.positions[proto].pos > data->lambdas.funcs[lam].start) - return proto-1; - } - return data->positions.elements-1; -} - -static void generate_code(FILE *out, lambda_source_t *source, size_t pos, size_t len, parse_data_t *data, size_t lam, bool source_only); -static void generate_functions(FILE *out, lambda_source_t *source, parse_data_t *data, size_t lam, size_t proto) { - size_t end = (proto+1) == data->positions.elements ? (size_t)-1 : data->positions.positions[proto+1].pos; - size_t first = lam; - for (; lam != data->lambdas.elements; ++lam) { - if (data->lambdas.funcs[lam].start > end) - break; - } - while (lam-- != first) { - lambda_t *lambda = &data->lambdas.funcs[lam]; - generate_begin(out, source, &data->lambdas, lam); - if (lambda->is_short) - fprintf(out, "{"); - generate_code(out, source, lambda->body.begin, lambda->body.length + 1, data, lam + 1, true); - if (lambda->is_short) - fprintf(out, "}"); - } - fprintf(out, "\n"); -} - -/* when generating the actual code we also take prototype-positioning into account */ -static void generate_code(FILE *out, lambda_source_t *source, size_t pos, size_t len, parse_data_t *data, size_t lam, bool source_only) { - /* we know that positions always has at least 1 element, the 0, so the first search is there */ - size_t proto = source_only ? data->positions.elements : next_prototype_position(data, lam, 1); - while (len) { - if (proto != data->positions.elements) { - lambda_position_t *lambdapos = &data->positions.positions[proto]; - size_t point = lambdapos->pos; - if (pos <= point && pos+len >= point) { - /* we insert prototypes here! */ - size_t length = point - pos; - fwrite(source->data + pos, length, 1, out); - generate_functions(out, source, data, lam, proto); - generate_marker(out, source->file, lambdapos->line, true); - len -= length; - pos += length; - } - } - - if (lam == data->lambdas.elements || data->lambdas.funcs[lam].start > pos + len) { - fwrite(source->data + pos, len, 1, out); - return; - } - - lambda_t *lambda = &data->lambdas.funcs[lam]; - size_t length = lambda->body.begin + lambda->body.length + 1 - pos; - - fwrite(source->data + pos, lambda->start - pos, 1, out); - fprintf(out, "(&lambda_%zu)", lam); - - len -= length; - pos += length; - - for (++lam; lam != data->lambdas.elements && data->lambdas.funcs[lam].start < pos; ++lam) - ; - proto = next_prototype_position(data, lam, proto); - } -} - - -static void generate(FILE *out, lambda_source_t *source) { - parse_data_t data; - lambda_vector_init(&data.lambdas, sizeof(data.lambdas.funcs[0])); - lambda_vector_init(&data.positions, sizeof(data.positions.positions[0])); - if (parse(source, &data, 0, PARSE_NORMAL, false) == ERROR) { - lambda_vector_destroy(&data.lambdas); - lambda_vector_destroy(&data.positions); - return; - } - - generate_marker(out, source->file, 1, false); - - generate_code(out, source, 0, source->length, &data, 0, false); - - /* there are cases where we get no newline at the end of the file */ - fprintf(out, "\n"); - - lambda_vector_destroy(&data.lambdas); - lambda_vector_destroy(&data.positions); -} - -static void usage(const char *prog, FILE *out) { - fprintf(out, "usage: %s [options] []\n", prog); - fprintf(out, - "options:\n" - " -h, --help print this help message\n" - " -V, --version show the current program version\n" - " -k, --keyword=WORD change the lambda keyword to WORD\n" - " -o, --output=FILE write to FILE instead of stdout\n" - " -s enable shortened syntax (default)\n" - " -S disable shortened syntax\n"); -} - -static void version(FILE *out) { - fprintf(out, "lambdapp 0.1\n"); -} - -/* returns false when the parameter doesn't match, - * returns true and sets argarg when the parameter does match, - * returns true and sets arg to -1 on error - */ -static bool isparam(int argc, char **argv, int *arg, char sh, const char *lng, char **argarg) { - if (argv[*arg][0] != '-') - return false; - /* short version */ - if (argv[*arg][1] == sh) { - if (argv[*arg][2]) { - *argarg = argv[*arg]+2; - return true; - } - ++*arg; - if (*arg == argc) { - fprintf(stderr, "%s: option -%c requires an argument\n", argv[0], sh); - usage(argv[0], stderr); - *arg = -1; - return true; - } - *argarg = argv[*arg]; - return true; - } - /* long version */ - if (argv[*arg][1] != '-') - return false; - size_t len = strlen(lng); - if (strncmp(argv[*arg]+2, lng, len)) - return false; - if (argv[*arg][len+2] == '=') { - *argarg = argv[*arg] + 3 + len; - return true; - } - if (!argv[*arg][len+2]) { - ++*arg; - if (*arg == argc) { - fprintf(stderr, "%s: option --%s requires an argument\n", argv[0], lng); - usage(argv[0], stderr); - *arg = -1; - return true; - } - *argarg = argv[*arg]; - return true; - } - return false; -} - -int main(int argc, char **argv) { - lambda_source_t source; - const char *file = NULL; - const char *output = NULL; - FILE *outfile = stdout; - - lambda_source_init(&source); - - int i = 1; - for (; i != argc; ++i) { - char *argarg; - - if (!strcmp(argv[i], "--")) { - ++i; - break; - } - if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { - usage(argv[0], stdout); - return 0; - } - if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) { - version(stdout); - return 0; - } - if (!strcmp(argv[i], "-s")) { - source.short_enabled = true; - continue; - } - if (!strcmp(argv[i], "-S")) { - source.short_enabled = false; - continue; - } - if (isparam(argc, argv, &i, 'k', "keyword", &argarg)) { - if (i < 0) - return 1; - source.keyword = argarg; - continue; - } - if (isparam(argc, argv, &i, 'o', "output", &argarg)) { - if (i < 0) - return 1; - output = argarg; - continue; - } - if (argv[i][0] == '-') { - fprintf(stderr, "%s: unrecognized option: %s\n", argv[0], argv[i]); - usage(argv[0], stderr); - return 1; - } - if (file) { - fprintf(stderr, "%s: only 1 file allowed\n", argv[0]); - usage(argv[0], stderr); - return 1; - } - file = argv[i]; - } - if (!file && i != argc) - file = argv[i++]; - if (i != argc) { - fprintf(stderr, "%s: only 1 file allowed\n", argv[0]); - usage(argv[0], stderr); - return 1; - } - - source.file = file ? file : ""; - if (!parse_open(&source, file ? fopen(file, "r") : stdin)) { - fprintf(stderr, "failed to open file %s %s\n", source.file, strerror(errno)); - return 1; - } - - source.keylength = strlen(source.keyword); - - if (output) { - outfile = fopen(output, "w"); - if (!outfile) { - fprintf(stderr, "failed to open file %s: %s\n", output, strerror(errno)); - return 1; - } - } - generate(outfile, &source); - if (outfile != stdout) - fclose(outfile); - parse_close(&source); - - return 0; -}