parser-helper: Add utility class for flex/bison based parsers

This commit is contained in:
Tobias Brunner 2014-03-07 17:04:01 +01:00
parent 3784633fa5
commit 3cb8016f0e
4 changed files with 408 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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 <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 "parser_helper.h"
#include <collections/array.h>
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;
}

View File

@ -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 <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.
*/
/**
* @defgroup parser_helper parser_helper
* @{ @ingroup utils
*/
#ifndef PARSER_HELPER_H_
#define PARSER_HELPER_H_
#include <collections/array.h>
#include <bio/bio_writer.h>
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.
*
* <code>PREFIX</code> equals whatever is configure with
* <code>%option prefix</code> resp. <code>%name-prefix</code>.
*/
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 <code>PREFIXget_lineno</code>.
*
* @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 <code>\<\<EOF\>\></code> 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
* <code>YY_CURRENT_BUFFER</code> 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_ @}*/