diff options
Diffstat (limited to 'src/libstrongswan/utils/parser_helper.c')
-rw-r--r-- | src/libstrongswan/utils/parser_helper.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/src/libstrongswan/utils/parser_helper.c b/src/libstrongswan/utils/parser_helper.c new file mode 100644 index 000000000..4c6aa251f --- /dev/null +++ b/src/libstrongswan/utils/parser_helper.c @@ -0,0 +1,261 @@ +/* + * 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 <limits.h> +#include <ctype.h> +#include <stdarg.h> + +#include "parser_helper.h" + +#include <collections/array.h> + +typedef struct private_parser_helper_t private_parser_helper_t; +typedef struct parser_helper_file_t parser_helper_file_t; + +struct private_parser_helper_t { + + /** + * Public interface. + */ + parser_helper_t public; + + /** + * Stack of included files, as parser_helper_file_t. + */ + array_t *files; + + /** + * Helper for parsing strings. + */ + bio_writer_t *writer; +}; + +struct parser_helper_file_t { + + /** + * File name + */ + char *name; + + /** + * File stream + */ + FILE *file; + + /** + * Enumerator of paths matching the most recent inclusion pattern. + */ + enumerator_t *matches; +}; + +/** + * Destroy the given file data. + */ +static void parser_helper_file_destroy(parser_helper_file_t *this) +{ + if (this->file) + { + fclose(this->file); + } + free(this->name); + DESTROY_IF(this->matches); + free(this); +} + +/** + * Returns the current file, if any. + */ +static parser_helper_file_t *current_file(private_parser_helper_t *this) +{ + parser_helper_file_t *file; + + array_get(this->files, ARRAY_TAIL, &file); + if (file->name) + { + return file; + } + return NULL; +} + +METHOD(parser_helper_t, file_next, FILE*, + private_parser_helper_t *this) +{ + parser_helper_file_t *file, *next; + char *name; + + array_get(this->files, ARRAY_TAIL, &file); + if (!file->matches && file->name) + { + 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, + .name = strdup(name), + .file = fopen(name, "r"), + ); + + if (next->file) + { + array_insert(this->files, ARRAY_TAIL, next); + return next->file; + } + PARSER_DBG2(&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) +{ + 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->name || path_absolute(pattern)) + { /* 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->name); + if (snprintf(pat, sizeof(pat), "%s%s%s", dir, DIRECTORY_SEPARATOR, + 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 + */ +void parser_helper_log(int level, parser_helper_t *ctx, char *fmt, ...) +{ + private_parser_helper_t *this = (private_parser_helper_t*)ctx; + parser_helper_file_t *file; + char msg[8192]; + va_list args; + int line; + + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + + file = current_file(this); + line = ctx->get_lineno ? ctx->get_lineno(ctx->scanner) : 0; + if (file) + { + dbg(DBG_CFG, level, "%s:%d: %s", file->name, line, msg); + } + else + { + dbg(DBG_CFG, level, "%s", msg); + } +} + +/** + * Described in header + */ +parser_helper_t *parser_helper_create(void *context) +{ + private_parser_helper_t *this; + parser_helper_file_t *sentinel; + + INIT(this, + .public = { + .context = context, + .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, + .name = NULL, + ); + array_insert(this->files, ARRAY_TAIL, sentinel); + + return &this->public; +} |