summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2017-06-11 13:33:18 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2017-06-11 13:33:18 +0100
commit768988d88470ffc1c64c35d6f9d3c37a9a6f75da (patch)
treee1af2f6bf6b69583bdf25e3cbb83e2c842d925d8 /src
parent602a6b133f83e2349a4b233536fdca90446638cd (diff)
downloadlibnslog-768988d88470ffc1c64c35d6f9d3c37a9a6f75da.tar.gz
libnslog-768988d88470ffc1c64c35d6f9d3c37a9a6f75da.tar.bz2
Simple parser for filter syntax
Diffstat (limited to 'src')
-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
4 files changed, 399 insertions, 0 deletions
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
--- /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
--- /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;
+}