parser-helper: Add utility class for flex/bison based parsers
authorTobias Brunner <tobias@strongswan.org>
Fri, 7 Mar 2014 16:04:01 +0000 (17:04 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 15 May 2014 09:28:06 +0000 (11:28 +0200)
src/libstrongswan/Android.mk
src/libstrongswan/Makefile.am
src/libstrongswan/utils/parser_helper.c [new file with mode: 0644]
src/libstrongswan/utils/parser_helper.h [new file with mode: 0644]

index 5f22bf1..ccff30b 100644 (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
 
index d202b02..e7f5ab0 100644 (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
diff --git a/src/libstrongswan/utils/parser_helper.c b/src/libstrongswan/utils/parser_helper.c
new file mode 100644 (file)
index 0000000..fb549a5
--- /dev/null
@@ -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;
+}
diff --git a/src/libstrongswan/utils/parser_helper.h b/src/libstrongswan/utils/parser_helper.h
new file mode 100644 (file)
index 0000000..741582d
--- /dev/null
@@ -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_ @}*/