summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2017-06-11 12:33:18 (GMT)
committer Daniel Silverstone <dsilvers@digital-scurf.org>2017-06-11 12:33:18 (GMT)
commit768988d88470ffc1c64c35d6f9d3c37a9a6f75da (patch)
treee1af2f6bf6b69583bdf25e3cbb83e2c842d925d8
parent602a6b133f83e2349a4b233536fdca90446638cd (diff)
downloadlibnslog-768988d88470ffc1c64c35d6f9d3c37a9a6f75da.tar.gz
libnslog-768988d88470ffc1c64c35d6f9d3c37a9a6f75da.tar.bz2
Simple parser for filter syntax
-rw-r--r--include/nslog/nslog.h7
-rw-r--r--src/Makefile34
-rw-r--r--src/filter-lexer.l84
-rw-r--r--src/filter-parser.y197
-rw-r--r--src/filter.c84
-rw-r--r--test/Makefile2
-rw-r--r--test/basictests.c43
-rw-r--r--test/parse.c25
8 files changed, 468 insertions, 8 deletions
diff --git a/include/nslog/nslog.h b/include/nslog/nslog.h
index fc3ead4..10b90a8 100644
--- a/include/nslog/nslog.h
+++ b/include/nslog/nslog.h
@@ -30,6 +30,7 @@ typedef enum {
const char *nslog_level_name(nslog_level level);
#define NSLOG_LEVEL_DD NSLOG_LEVEL_DEEPDEBUG
+#define NSLOG_LEVEL_DBG NSLOG_LEVEL_DEBUG
#define NSLOG_LEVEL_CHAT NSLOG_LEVEL_VERBOSE
#define NSLOG_LEVEL_WARN NSLOG_LEVEL_WARNING
#define NSLOG_LEVEL_ERR NSLOG_LEVEL_ERROR
@@ -105,6 +106,7 @@ typedef enum {
NSLOG_NO_ERROR = 0,
NSLOG_NO_MEMORY = 1,
NSLOG_UNCORKED = 2,
+ NSLOG_PARSE_ERROR = 3,
} nslog_error;
typedef void (*nslog_callback)(void *context, nslog_entry_context_t *ctx,
@@ -145,4 +147,9 @@ nslog_filter_t *nslog_filter_unref(nslog_filter_t *filter);
nslog_error nslog_filter_set_active(nslog_filter_t *filter,
nslog_filter_t **prev);
+char *nslog_filter_sprintf(nslog_filter_t *filter);
+
+nslog_error nslog_filter_from_text(const char *input,
+ nslog_filter_t **output);
+
#endif /* NSLOG_NSLOG_H_ */
diff --git a/src/Makefile b/src/Makefile
index 7fbad1c..3ca70cd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,3 +1,37 @@
DIR_SOURCES := core.c filter.c
+CFLAGS := $(CFLAGS) -I$(BUILDDIR) -Isrc/
+
+SOURCES := $(SOURCES) $(BUILDDIR)/filter-parser.c $(BUILDDIR)/filter-lexer.c
+
+$(BUILDDIR)/%-lexer.c $(BUILDDIR)/%-lexer.h: src/%-lexer.l
+ $(VQ)$(ECHO) " FLEX: $<"
+ $(Q)$(FLEX) --outfile=$(BUILDDIR)/$(*F)-lexer.c --header-file=$(BUILDDIR)/$(*F)-lexer.h $<
+
+$(BUILDDIR)/%-lexer.c: $(BUILDDIR)/%-parser.h
+
+# Bison 3.0 and later require api.prefix in curly braces
+# Bison 2.6 and later require api.prefix
+# Bison 2.5 and earlier require name-prefix switch
+bisonvsn := $(word 4,$(shell $(BISON) --version))
+bisonmaj := $(word 1,$(subst ., ,$(bisonvsn)))
+bisonmin := $(word 2,$(subst ., ,$(bisonvsn)))
+ifeq ($(bisonmaj),1)
+ BISON_DEFINES = --name-prefix=$(*F)_
+else
+ ifeq ($(bisonmaj),2)
+ ifneq ($(findstring $(bisonmin),"0 1 2 3 4 5"),)
+ BISON_DEFINES = --name-prefix=$(*F)_
+ else
+ BISON_DEFINES = --define=api.prefix=$(*F)_
+ endif
+ else
+ BISON_DEFINES = --define=api.prefix={$(*F)_}
+ endif
+endif
+
+$(BUILDDIR)/%-parser.c $(BUILDDIR)/%-parser.h: src/%-parser.y
+ $(VQ)$(ECHO) " BISON: $<"
+ $(Q)$(BISON) -d -t $(BISON_DEFINES) --report=all --output=$(BUILDDIR)/$(*F)-parser.c --defines=$(BUILDDIR)/$(*F)-parser.h $<
+
include $(NSBUILD)/Makefile.subdir
diff --git a/src/filter-lexer.l b/src/filter-lexer.l
new file mode 100644
index 0000000..5f75d29
--- a/dev/null
+++ b/src/filter-lexer.l
@@ -0,0 +1,84 @@
+%{
+
+/* This is a lexer for libnslog filter syntax
+ *
+ * This file is part of libnslog.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2017 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nslog/nslog.h"
+
+#include "filter-parser.h"
+
+/* Ensure compatability with bison 2.6 and later */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED && defined FILTER_STYPE_IS_DECLARED
+#define YYSTYPE FILTER_STYPE
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED && defined FILTER_LTYPE_IS_DECLARED
+#define YYLTYPE FILTER_LTYPE
+#endif
+
+%}
+
+
+/* lexer options */
+%option never-interactive
+%option bison-bridge
+%option bison-locations
+%option warn
+%option prefix="filter_"
+%option nounput
+%option noinput
+%option noyywrap
+
+whitespace [ \t]+
+
+pattern [^ \t]+
+
+%x st_patt
+
+%%
+
+{whitespace} { /* nothing */ }
+
+level: { return T_LEVEL_SPECIFIER; }
+lvl: { return T_LEVEL_SPECIFIER; }
+
+cat: { BEGIN(st_patt); return T_CATEGORY_SPECIFIER; }
+category: { BEGIN(st_patt); return T_CATEGORY_SPECIFIER; }
+file: { BEGIN(st_patt); return T_FILENAME_SPECIFIER; }
+filename: { BEGIN(st_patt); return T_FILENAME_SPECIFIER; }
+dir: { BEGIN(st_patt); return T_DIRNAME_SPECIFIER; }
+dirname: { BEGIN(st_patt); return T_DIRNAME_SPECIFIER; }
+func: { BEGIN(st_patt); return T_FUNCNAME_SPECIFIER; }
+funcname: { BEGIN(st_patt); return T_FUNCNAME_SPECIFIER; }
+
+"&&" { return T_OP_AND; }
+"||" { return T_OP_OR; }
+
+DEEPDEBUG { yylval->level = NSLOG_LEVEL_DEEPDEBUG; return T_LEVEL; }
+DDEBUG { yylval->level = NSLOG_LEVEL_DEEPDEBUG; return T_LEVEL; }
+DD { yylval->level = NSLOG_LEVEL_DEEPDEBUG; return T_LEVEL; }
+DEBUG { yylval->level = NSLOG_LEVEL_DEBUG; return T_LEVEL; }
+DBG { yylval->level = NSLOG_LEVEL_DEBUG; return T_LEVEL; }
+VERBOSE { yylval->level = NSLOG_LEVEL_VERBOSE; return T_LEVEL; }
+CHAT { yylval->level = NSLOG_LEVEL_VERBOSE; return T_LEVEL; }
+INFO { yylval->level = NSLOG_LEVEL_INFO; return T_LEVEL; }
+WARNING { yylval->level = NSLOG_LEVEL_WARNING; return T_LEVEL; }
+WARN { yylval->level = NSLOG_LEVEL_WARNING; return T_LEVEL; }
+ERROR { yylval->level = NSLOG_LEVEL_ERROR; return T_LEVEL; }
+ERR { yylval->level = NSLOG_LEVEL_ERROR; return T_LEVEL; }
+CRITICAL { yylval->level = NSLOG_LEVEL_CRITICAL; return T_LEVEL; }
+CRIT { yylval->level = NSLOG_LEVEL_CRITICAL; return T_LEVEL; }
+
+<st_patt>{pattern} { yylval->patt = yytext; BEGIN(INITIAL); return T_PATTERN; }
+
+. { return (int) yytext[0]; }
diff --git a/src/filter-parser.y b/src/filter-parser.y
new file mode 100644
index 0000000..bb225b6
--- a/dev/null
+++ b/src/filter-parser.y
@@ -0,0 +1,197 @@
+%{
+/* This is a bison parser for libnslog's filter syntax
+ *
+ * This file is part of libnslog.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2017 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ */
+
+#include "nslog/nslog.h"
+#include <assert.h>
+
+#include "filter-parser.h"
+#include "filter-lexer.h"
+
+static void filter_error(FILTER_LTYPE *loc, nslog_filter_t **output, const char *msg)
+{
+ (void)loc;
+ (void)output;
+ (void)msg;
+}
+
+%}
+
+%locations
+%pure-parser
+%parse-param { nslog_filter_t **output }
+
+%union {
+ char *patt;
+ nslog_level level;
+ nslog_filter_t *filter;
+}
+
+%token <patt> T_PATTERN
+%token <level> T_LEVEL
+
+%token T_CATEGORY_SPECIFIER
+%token T_FILENAME_SPECIFIER
+%token T_LEVEL_SPECIFIER
+%token T_DIRNAME_SPECIFIER
+%token T_FUNCNAME_SPECIFIER
+
+%token T_OP_AND
+%token T_OP_OR
+
+%type <filter> level_filter
+%type <filter> category_filter
+%type <filter> filename_filter
+%type <filter> dirname_filter
+%type <filter> funcname_filter
+%type <filter> basic_filter
+
+%type <filter> and_filter
+%type <filter> or_filter
+%type <filter> xor_filter
+%type <filter> binary_filter
+%type <filter> not_filter
+
+%type <filter> filter
+%type <filter> toplevel
+
+%start toplevel
+
+%%
+
+ /*
+ part ::= [^: \t\n]+
+level-name ::= 'DEEPDEBUG' | 'DD' | 'DEBUG' | 'VERBOSE' | 'CHAT' |
+ 'WARNING' | 'WARN' | 'ERROR' | 'ERR' | 'CRITICAL' | 'CRIT'
+
+category-filter ::= 'cat:' part
+level-filter ::= 'level:' level-name
+file-filter ::= 'file:' part
+dir-filter ::= 'dir:' dir
+
+factor ::= category-filter | level-filter | file-filter | dir-filter |
+ '(' expression ')'
+
+op ::= '&&' | '||' | '^' | 'and' | 'or' | 'xor' | 'eor'
+
+term ::= factor {op factor}
+
+expression ::= term | '!' term
+ */
+
+level_filter:
+ T_LEVEL_SPECIFIER T_LEVEL
+ {
+ assert(nslog_filter_level_new($2, &$$) == NSLOG_NO_ERROR);
+ }
+ ;
+
+category_filter:
+ T_CATEGORY_SPECIFIER T_PATTERN
+ {
+ assert(nslog_filter_category_new($2, &$$) == NSLOG_NO_ERROR);
+ }
+ ;
+
+filename_filter:
+ T_FILENAME_SPECIFIER T_PATTERN
+ {
+ assert(nslog_filter_filename_new($2, &$$) == NSLOG_NO_ERROR);
+ }
+ ;
+
+dirname_filter:
+ T_DIRNAME_SPECIFIER T_PATTERN
+ {
+ assert(nslog_filter_dirname_new($2, &$$) == NSLOG_NO_ERROR);
+ }
+ ;
+
+funcname_filter:
+ T_FUNCNAME_SPECIFIER T_PATTERN
+ {
+ assert(nslog_filter_funcname_new($2, &$$) == NSLOG_NO_ERROR);
+ }
+ ;
+
+basic_filter:
+ level_filter
+ |
+ category_filter
+ |
+ filename_filter
+ |
+ dirname_filter
+ |
+ funcname_filter
+ ;
+
+and_filter:
+ '(' filter T_OP_AND filter ')'
+ {
+ assert(nslog_filter_and_new($2, $4, &$$) == NSLOG_NO_ERROR);
+ nslog_filter_unref($2);
+ nslog_filter_unref($4);
+ }
+ ;
+
+or_filter:
+ '(' filter T_OP_OR filter ')'
+ {
+ assert(nslog_filter_or_new($2, $4, &$$) == NSLOG_NO_ERROR);
+ nslog_filter_unref($2);
+ nslog_filter_unref($4);
+ }
+ ;
+
+xor_filter:
+ '(' filter '^' filter ')'
+ {
+ assert(nslog_filter_xor_new($2, $4, &$$) == NSLOG_NO_ERROR);
+ nslog_filter_unref($2);
+ nslog_filter_unref($4);
+ }
+ ;
+
+binary_filter:
+ and_filter
+ |
+ or_filter
+ |
+ xor_filter
+ ;
+
+not_filter:
+ '!' filter
+ {
+ assert(nslog_filter_not_new($2, &$$) == NSLOG_NO_ERROR);
+ nslog_filter_unref($2);
+ }
+ ;
+
+filter:
+ not_filter
+ |
+ binary_filter
+ |
+ basic_filter
+ ;
+
+toplevel:
+ filter
+ {
+ $$ = *output = $1;
+ }
+ |
+ error
+ {
+ (void)yylloc;
+ YYABORT ;
+ }
+ ;
diff --git a/src/filter.c b/src/filter.c
index 1dc4e81..0a478b1 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -14,6 +14,19 @@
#include "nslog_internal.h"
+#include "filter-parser.h"
+
+/* Ensure compatability with bison 2.6 and later */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED && defined FILTER_STYPE_IS_DECLARED
+#define YYSTYPE FILTER_STYPE
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED && defined FILTER_LTYPE_IS_DECLARED
+#define YYLTYPE FILTER_LTYPE
+#endif
+
+#include "filter-lexer.h"
+
typedef enum {
/* Fundamentals */
NSLFK_CATEGORY = 0,
@@ -308,3 +321,74 @@ bool nslog__filter_matches(nslog_entry_context_t *ctx)
return true;
return _nslog__filter_matches(ctx, nslog__active_filter);
}
+
+char *nslog_filter_sprintf(nslog_filter_t *filter)
+{
+ char *ret = NULL;
+ switch (filter->kind) {
+ case NSLFK_CATEGORY:
+ ret = calloc(filter->params.str.len + 5, 1);
+ sprintf(ret, "cat:%s", filter->params.str.ptr);
+ break;
+ case NSLFK_LEVEL: {
+ const char *lvl = nslog_level_name(filter->params.level);
+ ret = calloc(strlen(lvl) + 5, 1);
+ sprintf(ret, "lvl:%s", lvl);
+ break;
+ }
+ case NSLFK_FILENAME:
+ ret = calloc(filter->params.str.len + 6, 1);
+ sprintf(ret, "file:%s", filter->params.str.ptr);
+ break;
+ case NSLFK_DIRNAME:
+ ret = calloc(filter->params.str.len + 5, 1);
+ sprintf(ret, "dir:%s", filter->params.str.ptr);
+ break;
+ case NSLFK_FUNCNAME:
+ ret = calloc(filter->params.str.len + 6, 1);
+ sprintf(ret, "func:%s", filter->params.str.ptr);
+ break;
+ case NSLFK_AND:
+ case NSLFK_OR:
+ case NSLFK_XOR: {
+ char *left = nslog_filter_sprintf(filter->params.binary.input1);
+ char *right = nslog_filter_sprintf(filter->params.binary.input2);
+ const char *op =
+ (filter->kind == NSLFK_AND) ? "&&" :
+ (filter->kind == NSLFK_OR) ? "||" : "^";
+ ret = calloc(strlen(left) + strlen(right) + 7, 1);
+ sprintf(ret, "(%s %s %s)", left, op, right);
+ free(left);
+ free(right);
+ break;
+ }
+ case NSLFK_NOT: {
+ char *input = nslog_filter_sprintf(filter->params.unary_input);
+ ret = calloc(strlen(input) + 2, 1);
+ sprintf(ret, "!%s", input);
+ free(input);
+ break;
+ }
+ default:
+ assert("Unexpected kind" == NULL);
+ return strdup("***ERROR***");
+ }
+ return ret;
+}
+
+nslog_error nslog_filter_from_text(const char *input,
+ nslog_filter_t **output)
+{
+ int ret;
+ YY_BUFFER_STATE buffer = filter__scan_string((char *)input);
+ filter_push_buffer_state(buffer);
+ ret = filter_parse(output);
+ filter_lex_destroy();
+ switch (ret) {
+ case 0:
+ return NSLOG_NO_ERROR;
+ case 2:
+ return NSLOG_NO_MEMORY;
+ }
+ return NSLOG_PARSE_ERROR;
+}
diff --git a/test/Makefile b/test/Makefile
index 36d5092..5ebac01 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,3 +1,3 @@
-DIR_TEST_ITEMS := testrunner:testmain.c;basictests.c
+DIR_TEST_ITEMS := testrunner:testmain.c;basictests.c parse:parse.c
include $(NSBUILD)/Makefile.subdir
diff --git a/test/basictests.c b/test/basictests.c
index b83ed9f..d74df87 100644
--- a/test/basictests.c
+++ b/test/basictests.c
@@ -171,7 +171,7 @@ START_TEST (test_nslog_simple_filter_corked_message)
"Captured message wasn't correct filename");
fail_unless(strcmp(captured_context.funcname, "test_nslog_simple_filter_corked_message") == 0,
"Captured message wasn't correct function name");
-
+
}
END_TEST
@@ -198,7 +198,34 @@ START_TEST (test_nslog_simple_filter_uncorked_message)
"Captured message wasn't correct filename");
fail_unless(strcmp(captured_context.funcname, "test_nslog_simple_filter_uncorked_message") == 0,
"Captured message wasn't correct function name");
-
+
+}
+END_TEST
+
+START_TEST (test_nslog_basic_filter_sprintf)
+{
+ char *ct = nslog_filter_sprintf(cat_test);
+ fail_unless(ct != NULL, "Unable to sprintf");
+ fail_unless(strcmp(ct, "cat:test") == 0,
+ "Printed category test is wrong");
+ free(ct);
+ ct = nslog_filter_sprintf(cat_another);
+ fail_unless(ct != NULL, "Unable to sprintf");
+ fail_unless(strcmp(ct, "cat:another") == 0,
+ "Printed category another is wrong");
+ free(ct);
+}
+END_TEST
+
+START_TEST (test_nslog_parse_and_sprintf)
+{
+ nslog_filter_t *filt;
+ fail_unless(nslog_filter_from_text("cat:test", &filt) == NSLOG_NO_ERROR,
+ "Unable to parse cat:test");
+ char *ct = nslog_filter_sprintf(filt);
+ nslog_filter_unref(filt);
+ fail_unless(strcmp(ct, "cat:test") == 0,
+ "Printed parsed cat:test not right");
}
END_TEST
@@ -209,22 +236,24 @@ nslog_basic_suite(SRunner *sr)
{
Suite *s = suite_create("libnslog: Basic tests");
TCase *tc_basic = NULL;
-
+
tc_basic = tcase_create("Simple log checks, no filters");
-
+
tcase_add_checked_fixture(tc_basic, with_simple_context_setup,
with_simple_context_teardown);
tcase_add_test(tc_basic, test_nslog_trivial_corked_message);
tcase_add_test(tc_basic, test_nslog_trivial_uncorked_message);
suite_add_tcase(s, tc_basic);
-
+
tc_basic = tcase_create("Simple filter checks");
-
+
tcase_add_checked_fixture(tc_basic, with_simple_filter_context_setup,
with_simple_filter_context_teardown);
tcase_add_test(tc_basic, test_nslog_simple_filter_corked_message);
tcase_add_test(tc_basic, test_nslog_simple_filter_uncorked_message);
+ tcase_add_test(tc_basic, test_nslog_basic_filter_sprintf);
+ tcase_add_test(tc_basic, test_nslog_parse_and_sprintf);
suite_add_tcase(s, tc_basic);
-
+
srunner_add_suite(sr, s);
}
diff --git a/test/parse.c b/test/parse.c
new file mode 100644
index 0000000..9fca97b
--- a/dev/null
+++ b/test/parse.c
@@ -0,0 +1,25 @@
+#include "nslog/nslog.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: parse 'filtertext'\n");
+ return 1;
+ }
+ nslog_filter_t *filt;
+ nslog_error err;
+
+ err = nslog_filter_from_text(argv[1], &filt);
+ if (err != NSLOG_NO_ERROR) {
+ fprintf(stderr, "Unable to parse.\n");
+ return 2;
+ }
+ char *ct = nslog_filter_sprintf(filt);
+ filt = nslog_filter_unref(filt);
+ printf("%s\n", ct);
+ free(ct);
+ return 0;
+}