From: Tobias Brunner Date: Fri, 7 Mar 2014 16:04:01 +0000 (+0100) Subject: parser-helper: Add utility class for flex/bison based parsers X-Git-Tag: 5.2.0dr4~1^2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3cb8016f0e938e92ff680f8d6eef0c857dac5d75;p=thirdparty%2Fstrongswan.git parser-helper: Add utility class for flex/bison based parsers --- diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 5f22bf1148..ccff30bc05 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -37,7 +37,8 @@ threading/thread.c threading/thread_value.c \ threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \ -utils/printf_hook/printf_hook_builtin.c utils/test.c utils/utils/strerror.c +utils/parser_helper.c utils/test.c utils/utils/strerror.c \ +utils/printf_hook/printf_hook_builtin.c # adding the plugin source files diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index d202b023dd..e7f5ab05a0 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -35,7 +35,7 @@ threading/thread.c threading/thread_value.c \ threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \ -utils/test.c utils/utils/strerror.c +utils/parser_helper.c utils/test.c utils/utils/strerror.c if USE_DEV_HEADERS strongswan_includedir = ${dev_headers} @@ -82,7 +82,8 @@ utils/utils.h utils/chunk.h utils/debug.h utils/enum.h utils/identification.h \ utils/lexparser.h utils/optionsfrom.h utils/capabilities.h utils/backtrace.h \ utils/leak_detective.h utils/printf_hook/printf_hook.h \ utils/printf_hook/printf_hook_vstr.h utils/printf_hook/printf_hook_builtin.h \ -utils/test.h utils/integrity_checker.h utils/utils/strerror.h +utils/parser_helper.h utils/test.h utils/integrity_checker.h \ +utils/utils/strerror.h endif library.lo : $(top_builddir)/config.status diff --git a/src/libstrongswan/utils/parser_helper.c b/src/libstrongswan/utils/parser_helper.c new file mode 100644 index 0000000000..fb549a5729 --- /dev/null +++ b/src/libstrongswan/utils/parser_helper.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2014 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include + +#include "parser_helper.h" + +#include + +typedef struct private_parser_helper_t private_parser_helper_t; +typedef struct private_parser_helper_file_t private_parser_helper_file_t; + +struct private_parser_helper_t { + + /** + * Public interface. + */ + parser_helper_t public; + + /** + * Stack of included files, as private_parser_helper_file_t. + */ + array_t *files; + + /** + * Helper for parsing strings. + */ + bio_writer_t *writer; +}; + +struct private_parser_helper_file_t { + + /** + * File data. + */ + parser_helper_file_t public; + + /** + * Enumerator of paths matching the most recent inclusion pattern. + */ + enumerator_t *matches; +}; + +/** + * Destroy the given file data. + */ +static void parser_helper_file_destroy(private_parser_helper_file_t *this) +{ + if (this->public.file) + { + fclose(this->public.file); + } + free(this->public.name); + DESTROY_IF(this->matches); + free(this); +} + +METHOD(parser_helper_t, file_current, parser_helper_file_t*, + private_parser_helper_t *this) +{ + private_parser_helper_file_t *file; + + array_get(this->files, ARRAY_TAIL, &file); + if (file->public.name) + { + return &file->public; + } + return NULL; +} + +METHOD(parser_helper_t, file_next, parser_helper_file_t*, + private_parser_helper_t *this) +{ + private_parser_helper_file_t *file, *next; + char *name; + + array_get(this->files, ARRAY_TAIL, &file); + if (!file->matches) + { + array_remove(this->files, ARRAY_TAIL, NULL); + parser_helper_file_destroy(file); + /* continue with previous includes, if any */ + array_get(this->files, ARRAY_TAIL, &file); + } + if (file->matches) + { + while (file->matches->enumerate(file->matches, &name, NULL)) + { + INIT(next, + .public = { + .name = strdup(name), + .file = fopen(name, "r"), + }, + ); + + if (next->public.file) + { + array_insert(this->files, ARRAY_TAIL, next); + return &next->public; + } + PARSER_DBG1(&this->public, "unable to open '%s'", name); + parser_helper_file_destroy(next); + } + file->matches->destroy(file->matches); + file->matches = NULL; + } + return NULL; +} + +METHOD(parser_helper_t, file_include, void, + private_parser_helper_t *this, char *pattern) +{ + private_parser_helper_file_t *file; + char pat[PATH_MAX]; + + array_get(this->files, ARRAY_TAIL, &file); + if (!pattern || !*pattern) + { + PARSER_DBG1(&this->public, "no include pattern specified, ignored"); + file->matches = enumerator_create_empty(); + return; + } + + if (!file->public.name || pattern[0] == '/') + { /* absolute path */ + if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat)) + { + PARSER_DBG1(&this->public, "include pattern too long, ignored"); + file->matches = enumerator_create_empty(); + return; + } + } + else + { /* base relative paths to the directory of the current file */ + char *dir = path_dirname(file->public.name); + if (snprintf(pat, sizeof(pat), "%s/%s", dir, pattern) >= sizeof(pat)) + { + PARSER_DBG1(&this->public, "include pattern too long, ignored"); + free(dir); + file->matches = enumerator_create_empty(); + return; + } + free(dir); + } + + file->matches = enumerator_create_glob(pat); + if (!file->matches) + { /* if glob(3) is not available, try to load pattern directly */ + file->matches = enumerator_create_single(strdup(pat), free); + } +} + +METHOD(parser_helper_t, string_init, void, + private_parser_helper_t *this) +{ + chunk_t data; + + data = this->writer->extract_buf(this->writer); + chunk_free(&data); +} + +METHOD(parser_helper_t, string_add, void, + private_parser_helper_t *this, char *str) +{ + this->writer->write_data(this->writer, chunk_from_str(str)); +} + +METHOD(parser_helper_t, string_get, char*, + private_parser_helper_t *this) +{ + chunk_t data; + + this->writer->write_data(this->writer, chunk_from_chars('\0')); + data = this->writer->extract_buf(this->writer); + return data.ptr; +} + +METHOD(parser_helper_t, destroy, void, + private_parser_helper_t *this) +{ + array_destroy_function(this->files, (void*)parser_helper_file_destroy, NULL); + this->writer->destroy(this->writer); + free(this); +} + +/** + * Described in header + */ +parser_helper_t *parser_helper_create(void *context) +{ + private_parser_helper_t *this; + private_parser_helper_file_t *sentinel; + + INIT(this, + .public = { + .context = context, + .file_current = _file_current, + .file_include = _file_include, + .file_next = _file_next, + .string_init = _string_init, + .string_add = _string_add, + .string_get = _string_get, + .destroy = _destroy, + }, + .files = array_create(0, 0), + .writer = bio_writer_create(0), + ); + + INIT(sentinel, + .public = { + .name = NULL, + }, + ); + array_insert(this->files, ARRAY_TAIL, sentinel); + + return &this->public; +} diff --git a/src/libstrongswan/utils/parser_helper.h b/src/libstrongswan/utils/parser_helper.h new file mode 100644 index 0000000000..741582d2c5 --- /dev/null +++ b/src/libstrongswan/utils/parser_helper.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2014 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup parser_helper parser_helper + * @{ @ingroup utils + */ + +#ifndef PARSER_HELPER_H_ +#define PARSER_HELPER_H_ + +#include +#include + +typedef struct parser_helper_t parser_helper_t; +typedef struct parser_helper_file_t parser_helper_file_t; + +/** + * Helper class for flex/bison based parsers. + * + * PREFIX equals whatever is configure with + * %option prefix resp. %name-prefix. + */ +struct parser_helper_t { + + /** + * A user defined parser context object. + */ + const void *context; + + /** + * Opaque object allocated by the lexer, should be set with: + * @code + * PREFIXlex_init_extra(helper, &helper->scanner). + * @endcode + */ + void *scanner; + + /** + * Function to determine the current line number (defined by the lexer). + * + * Basically, this should be assigned to PREFIXget_lineno. + * + * @param scanner the lexer + * @return current line number + */ + int (*get_lineno)(void *scanner); + + /** + * Get the current file. + * + * @return current file, or NULL + */ + parser_helper_file_t *(*file_current)(parser_helper_t *this); + + /** + * Resolves the given include pattern, relative to the location of the + * current file. + * + * Call file_next() to open the next file. + * + * @param pattern file pattern + */ + void (*file_include)(parser_helper_t *this, char *pattern); + + /** + * Get the next file to process. + * + * This will return NULL if all files matching the most recent pattern + * have been handled. If there are other patterns the next call will then + * return the next file matching the previous pattern. + * + * When hitting \<\\> first call + * @code + * PREFIXpop_buffer_state(yyscanner); + * @endcode + * then call this method to check if there are more files to include for + * the most recent call to file_include(), if so, call + * @code + * PREFIXset_in(file->file, helper->scanner); + * PREFIXpush_buffer_state(PREFIX_create_buffer(file->file, YY_BUF_SIZE, + * helper->scanner), helper->scanner); + * @endcode + * + * If there are no more files to process check + * YY_CURRENT_BUFFER and if it is FALSE call yyterminate(). + * + * @return next file to process, or NULL (see comment) + */ + parser_helper_file_t *(*file_next)(parser_helper_t *this); + + /** + * Start parsing a string, discards any currently stored data. + */ + void (*string_init)(parser_helper_t *this); + + /** + * Append the given string. + * + * @param str string to append + */ + void (*string_add)(parser_helper_t *this, char *str); + + /** + * Extract the current string buffer as null-terminated string. Can only + * be called once per string. + * + * @return allocated string + */ + char *(*string_get)(parser_helper_t *this); + + /** + * Destroy this instance. + */ + void (*destroy)(parser_helper_t *this); +}; + +struct parser_helper_file_t { + + /** + * File name + */ + char *name; + + /** + * File stream + */ + FILE *file; +}; + +/** + * Log the given message either as error or warning + * + * @param level log level + * @param ctx current parser context + * @param fmt error message format + * @param ... additional arguments + */ +#define parser_helper_log(level, ctx, fmt, ...) ({ \ + parser_helper_file_t *_file = (ctx)->file_current(ctx); \ + int _line = (ctx)->get_lineno ? (ctx)->get_lineno((ctx)->scanner) : 0; \ + if (_file) {\ + DBG##level(DBG_CFG, "%s:%d: " fmt, _file->name, _line, ##__VA_ARGS__); \ + } else { \ + DBG##level(DBG_CFG, fmt, ##__VA_ARGS__); \ + } \ +}) + +#define PARSER_DBG1(ctx, fmt, ...) parser_helper_log(1, ctx, fmt, ##__VA_ARGS__) +#define PARSER_DBG2(ctx, fmt, ...) parser_helper_log(2, ctx, fmt, ##__VA_ARGS__) +#define PARSER_DBG3(ctx, fmt, ...) parser_helper_log(3, ctx, fmt, ##__VA_ARGS__) + +/** + * Create a parser helper object + * + * @param context user defined parser context + * @return parser helper + */ +parser_helper_t *parser_helper_create(void *context); + +#endif /** PARSER_HELPER_H_ @}*/