|
Packit |
1422b7 |
/* samp.c -- code for ln_samp objects.
|
|
Packit |
1422b7 |
* This code handles rulebase processing. Rulebases have been called
|
|
Packit |
1422b7 |
* "sample bases" in the early days of liblognorm, thus the name.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* Copyright 2010-2018 by Rainer Gerhards and Adiscon GmbH.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* Modified by Pavel Levshin (pavel@levshin.spb.ru) in 2013
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* This file is part of liblognorm.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* This library is free software; you can redistribute it and/or
|
|
Packit |
1422b7 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
1422b7 |
* License as published by the Free Software Foundation; either
|
|
Packit |
1422b7 |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
1422b7 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
1422b7 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
1422b7 |
* Lesser General Public License for more details.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
1422b7 |
* License along with this library; if not, write to the Free Software
|
|
Packit |
1422b7 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* A copy of the LGPL v2.1 can be found in the file "COPYING" in this distribution.
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
#include "config.h"
|
|
Packit |
1422b7 |
#include <stdlib.h>
|
|
Packit |
1422b7 |
#include <stdio.h>
|
|
Packit |
1422b7 |
#include <stdarg.h>
|
|
Packit |
1422b7 |
#include <string.h>
|
|
Packit |
1422b7 |
#include <assert.h>
|
|
Packit |
1422b7 |
#include <ctype.h>
|
|
Packit |
1422b7 |
#include <errno.h>
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
#include "liblognorm.h"
|
|
Packit |
1422b7 |
#include "lognorm.h"
|
|
Packit |
1422b7 |
#include "samp.h"
|
|
Packit |
1422b7 |
#include "internal.h"
|
|
Packit |
1422b7 |
#include "parser.h"
|
|
Packit |
1422b7 |
#include "pdag.h"
|
|
Packit |
1422b7 |
#include "v1_liblognorm.h"
|
|
Packit |
1422b7 |
#include "v1_ptree.h"
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
void
|
|
Packit |
1422b7 |
ln_sampFree(ln_ctx __attribute__((unused)) ctx, struct ln_samp *samp)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
free(samp);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
ln_parseLegacyFieldDescr(ln_ctx ctx,
|
|
Packit |
1422b7 |
const char *const buf,
|
|
Packit |
1422b7 |
const size_t lenBuf,
|
|
Packit |
1422b7 |
size_t *bufOffs,
|
|
Packit |
1422b7 |
es_str_t **str,
|
|
Packit |
1422b7 |
json_object **prscnf)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 0;
|
|
Packit |
1422b7 |
char *cstr; /* for debug mode strings */
|
|
Packit |
1422b7 |
char *ftype = NULL;
|
|
Packit |
1422b7 |
char name[MAX_FIELDNAME_LEN];
|
|
Packit |
1422b7 |
size_t iDst;
|
|
Packit |
1422b7 |
struct json_object *json = NULL;
|
|
Packit |
1422b7 |
char *ed = NULL;
|
|
Packit |
1422b7 |
es_size_t i = *bufOffs;
|
|
Packit |
1422b7 |
es_str_t *edata = NULL;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
for( iDst = 0
|
|
Packit |
1422b7 |
; iDst < (MAX_FIELDNAME_LEN - 1) && i < lenBuf && buf[i] != ':'
|
|
Packit |
1422b7 |
; ++iDst) {
|
|
Packit |
1422b7 |
name[iDst] = buf[i++];
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
name[iDst] = '\0';
|
|
Packit |
1422b7 |
if(iDst == (MAX_FIELDNAME_LEN - 1)) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "field name too long in: %s", buf+(*bufOffs));
|
|
Packit |
1422b7 |
FAIL(LN_INVLDFDESCR);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if(i == lenBuf) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "field definition wrong in: %s", buf+(*bufOffs));
|
|
Packit |
1422b7 |
FAIL(LN_INVLDFDESCR);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(iDst == 0) {
|
|
Packit |
1422b7 |
FAIL(LN_INVLDFDESCR);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(ctx->debug) {
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "parsed field: '%s'", name);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(buf[i] != ':') {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "missing colon in: %s", buf+(*bufOffs));
|
|
Packit |
1422b7 |
FAIL(LN_INVLDFDESCR);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
++i; /* skip ':' */
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* parse and process type (trailing whitespace must be trimmed) */
|
|
Packit |
1422b7 |
es_emptyStr(*str);
|
|
Packit |
1422b7 |
size_t j = i;
|
|
Packit |
1422b7 |
/* scan for terminator */
|
|
Packit |
1422b7 |
while(j < lenBuf && buf[j] != ':' && buf[j] != '{' && buf[j] != '%')
|
|
Packit |
1422b7 |
++j;
|
|
Packit |
1422b7 |
/* now trim trailing space backwards */
|
|
Packit |
1422b7 |
size_t next = j;
|
|
Packit |
1422b7 |
--j;
|
|
Packit |
1422b7 |
while(j >= i && isspace(buf[j]))
|
|
Packit |
1422b7 |
--j;
|
|
Packit |
1422b7 |
/* now copy */
|
|
Packit |
1422b7 |
while(i <= j) {
|
|
Packit |
1422b7 |
CHKR(es_addChar(str, buf[i++]));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
/* finally move i to consumed position */
|
|
Packit |
1422b7 |
i = next;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(i == lenBuf) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "premature end (missing %%?) in: %s", buf+(*bufOffs));
|
|
Packit |
1422b7 |
FAIL(LN_INVLDFDESCR);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ftype = es_str2cstr(*str, NULL);
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "field type '%s', i %d", ftype, i);
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(buf[i] == '{') {
|
|
Packit |
1422b7 |
struct json_tokener *tokener = json_tokener_new();
|
|
Packit |
1422b7 |
json = json_tokener_parse_ex(tokener, buf+i, (int) (lenBuf - i));
|
|
Packit |
1422b7 |
if(json == NULL) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "invalid json in '%s'", buf+i);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
i += tokener->char_offset;
|
|
Packit |
1422b7 |
json_tokener_free(tokener);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(buf[i] == '%') {
|
|
Packit |
1422b7 |
i++;
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
/* parse extra data */
|
|
Packit |
1422b7 |
CHKN(edata = es_newStr(8));
|
|
Packit |
1422b7 |
i++;
|
|
Packit |
1422b7 |
while(i < lenBuf) {
|
|
Packit |
1422b7 |
if(buf[i] == '%') {
|
|
Packit |
1422b7 |
++i;
|
|
Packit |
1422b7 |
break; /* end of field */
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
CHKR(es_addChar(&edata, buf[i++]));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
es_unescapeStr(edata);
|
|
Packit |
1422b7 |
if(ctx->debug) {
|
|
Packit |
1422b7 |
cstr = es_str2cstr(edata, NULL);
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "parsed extra data: '%s'", cstr);
|
|
Packit |
1422b7 |
free(cstr);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
struct json_object *val;
|
|
Packit |
1422b7 |
*prscnf = json_object_new_object();
|
|
Packit |
1422b7 |
CHKN(val = json_object_new_string(name));
|
|
Packit |
1422b7 |
json_object_object_add(*prscnf, "name", val);
|
|
Packit |
1422b7 |
CHKN(val = json_object_new_string(ftype));
|
|
Packit |
1422b7 |
json_object_object_add(*prscnf, "type", val);
|
|
Packit |
1422b7 |
if(edata != NULL) {
|
|
Packit |
1422b7 |
ed = es_str2cstr(edata, " ");
|
|
Packit |
1422b7 |
CHKN(val = json_object_new_string(ed));
|
|
Packit |
1422b7 |
json_object_object_add(*prscnf, "extradata", val);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if(json != NULL) {
|
|
Packit |
1422b7 |
/* now we need to merge the json params into the main object */
|
|
Packit |
1422b7 |
struct json_object_iterator it = json_object_iter_begin(json);
|
|
Packit |
1422b7 |
struct json_object_iterator itEnd = json_object_iter_end(json);
|
|
Packit |
1422b7 |
while (!json_object_iter_equal(&it, &itEnd)) {
|
|
Packit |
1422b7 |
struct json_object *const v = json_object_iter_peek_value(&it);
|
|
Packit |
1422b7 |
json_object_get(v);
|
|
Packit |
1422b7 |
json_object_object_add(*prscnf, json_object_iter_peek_name(&it), v);
|
|
Packit |
1422b7 |
json_object_iter_next(&it);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
*bufOffs = i;
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
free(ed);
|
|
Packit |
1422b7 |
if(edata != NULL)
|
|
Packit |
1422b7 |
es_deleteStr(edata);
|
|
Packit |
1422b7 |
free(ftype);
|
|
Packit |
1422b7 |
if(json != NULL)
|
|
Packit |
1422b7 |
json_object_put(json);
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Extract a field description from a sample.
|
|
Packit |
1422b7 |
* The field description is added to the tail of the current
|
|
Packit |
1422b7 |
* subtree's field list. The parse buffer must be position on the
|
|
Packit |
1422b7 |
* leading '%' that starts a field definition. It is a program error
|
|
Packit |
1422b7 |
* if this condition is not met.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* Note that we break up the object model and access ptree members
|
|
Packit |
1422b7 |
* directly. Let's consider us a friend of ptree. This is necessary
|
|
Packit |
1422b7 |
* to optimize the structure for a high-speed parsing process.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] str a temporary work string. This is passed in to save the
|
|
Packit |
1422b7 |
* creation overhead
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
addFieldDescr(ln_ctx ctx, struct ln_pdag **pdag, es_str_t *rule,
|
|
Packit |
1422b7 |
size_t *bufOffs, es_str_t **str)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 0;
|
|
Packit |
1422b7 |
es_size_t i = *bufOffs;
|
|
Packit |
1422b7 |
char *ftype = NULL;
|
|
Packit |
1422b7 |
const char *buf;
|
|
Packit |
1422b7 |
es_size_t lenBuf;
|
|
Packit |
1422b7 |
struct json_object *prs_config = NULL;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
buf = (const char*)es_getBufAddr(rule);
|
|
Packit |
1422b7 |
lenBuf = es_strlen(rule);
|
|
Packit |
1422b7 |
assert(buf[i] == '%');
|
|
Packit |
1422b7 |
++i; /* "eat" ':' */
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* skip leading whitespace in field name */
|
|
Packit |
1422b7 |
while(i < lenBuf && isspace(buf[i]))
|
|
Packit |
1422b7 |
++i;
|
|
Packit |
1422b7 |
/* check if we have new-style json config */
|
|
Packit |
1422b7 |
if(buf[i] == '{' || buf[i] == '[') {
|
|
Packit |
1422b7 |
struct json_tokener *tokener = json_tokener_new();
|
|
Packit |
1422b7 |
prs_config = json_tokener_parse_ex(tokener, buf+i, (int) (lenBuf - i));
|
|
Packit |
1422b7 |
i += tokener->char_offset;
|
|
Packit |
1422b7 |
json_tokener_free(tokener);
|
|
Packit |
1422b7 |
if(prs_config == NULL || i == lenBuf || buf[i] != '%') {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "invalid json in '%s'", buf+i);
|
|
Packit |
1422b7 |
r = -1;
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
*bufOffs = i+1; /* eat '%' - if above ensures it is present */
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
*bufOffs = i;
|
|
Packit |
1422b7 |
CHKR(ln_parseLegacyFieldDescr(ctx, buf, lenBuf, bufOffs, str, &prs_config));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
CHKR(ln_pdagAddParser(ctx, pdag, prs_config));
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
free(ftype);
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Construct a literal parser json definition.
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static json_object *
|
|
Packit |
1422b7 |
newLiteralParserJSONConf(char lit)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
char buf[] = "x";
|
|
Packit |
1422b7 |
buf[0] = lit;
|
|
Packit |
1422b7 |
struct json_object *val;
|
|
Packit |
1422b7 |
struct json_object *prscnf = json_object_new_object();
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
val = json_object_new_string("literal");
|
|
Packit |
1422b7 |
json_object_object_add(prscnf, "type", val);
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
val = json_object_new_string(buf);
|
|
Packit |
1422b7 |
json_object_object_add(prscnf, "text", val);
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
return prscnf;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Parse a Literal string out of the template and add it to the tree.
|
|
Packit |
1422b7 |
* This function is used to create the unoptimized tree. So we do
|
|
Packit |
1422b7 |
* one node for each character. These will be compacted by the optimizer
|
|
Packit |
1422b7 |
* in a later stage. The advantage is that we do not need to care about
|
|
Packit |
1422b7 |
* splitting the tree. As such the processing is fairly simple:
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* for each character in literal (left-to-right):
|
|
Packit |
1422b7 |
* create literal parser object o
|
|
Packit |
1422b7 |
* add new DAG node o, advance to it
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx the context
|
|
Packit |
1422b7 |
* @param[in/out] subtree on entry, current subtree, on exist newest
|
|
Packit |
1422b7 |
* deepest subtree
|
|
Packit |
1422b7 |
* @param[in] rule string with current rule
|
|
Packit |
1422b7 |
* @param[in/out] bufOffs parse pointer, up to which offset is parsed
|
|
Packit |
1422b7 |
* (is updated so that it points to first char after consumed
|
|
Packit |
1422b7 |
* string on exit).
|
|
Packit |
1422b7 |
* @param str a work buffer, provided to prevent creation of a new object
|
|
Packit |
1422b7 |
* @return 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
parseLiteral(ln_ctx ctx, struct ln_pdag **pdag, es_str_t *rule,
|
|
Packit |
1422b7 |
size_t *const __restrict__ bufOffs, es_str_t **str)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 0;
|
|
Packit |
1422b7 |
size_t i = *bufOffs;
|
|
Packit |
1422b7 |
unsigned char *buf = es_getBufAddr(rule);
|
|
Packit |
1422b7 |
const size_t lenBuf = es_strlen(rule);
|
|
Packit |
1422b7 |
const char *cstr = NULL;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
es_emptyStr(*str);
|
|
Packit |
1422b7 |
while(i < lenBuf) {
|
|
Packit |
1422b7 |
if(buf[i] == '%') {
|
|
Packit |
1422b7 |
if(i+1 < lenBuf && buf[i+1] != '%') {
|
|
Packit |
1422b7 |
break; /* field start is end of literal */
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if (++i == lenBuf) break;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
CHKR(es_addChar(str, buf[i]));
|
|
Packit |
1422b7 |
++i;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
es_unescapeStr(*str);
|
|
Packit |
1422b7 |
cstr = es_str2cstr(*str, NULL);
|
|
Packit |
1422b7 |
if(ctx->debug) {
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "parsed literal: '%s'", cstr);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
*bufOffs = i;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* we now add the string to the tree */
|
|
Packit |
1422b7 |
for(i = 0 ; cstr[i] != '\0' ; ++i) {
|
|
Packit |
1422b7 |
struct json_object *const prscnf =
|
|
Packit |
1422b7 |
newLiteralParserJSONConf(cstr[i]);
|
|
Packit |
1422b7 |
CHKN(prscnf);
|
|
Packit |
1422b7 |
CHKR(ln_pdagAddParser(ctx, pdag, prscnf));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
free((void*)cstr);
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* Implementation note:
|
|
Packit |
1422b7 |
* We read in the sample, and split it into chunks of literal text and
|
|
Packit |
1422b7 |
* fields. Each literal text is added as whole to the tree, as is each
|
|
Packit |
1422b7 |
* field individually. To do so, we keep track of our current subtree
|
|
Packit |
1422b7 |
* root, which changes whenever a new part of the tree is build. It is
|
|
Packit |
1422b7 |
* set to the then-lowest part of the tree, where the next step sample
|
|
Packit |
1422b7 |
* data is to be added.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* This function processes the whole string or returns an error.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* format: literal1%field:type:extra-data%literal2
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @returns the new dag root (or NULL in case of error)
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
addSampToTree(ln_ctx ctx,
|
|
Packit |
1422b7 |
es_str_t *rule,
|
|
Packit |
1422b7 |
ln_pdag *dag,
|
|
Packit |
1422b7 |
struct json_object *tagBucket)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
es_str_t *str = NULL;
|
|
Packit |
1422b7 |
size_t i;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
CHKN(str = es_newStr(256));
|
|
Packit |
1422b7 |
i = 0;
|
|
Packit |
1422b7 |
while(i < es_strlen(rule)) {
|
|
Packit |
1422b7 |
LN_DBGPRINTF(ctx, "addSampToTree %zu of %d", i, es_strlen(rule));
|
|
Packit |
1422b7 |
CHKR(parseLiteral(ctx, &dag, rule, &i, &str);;
|
|
Packit |
1422b7 |
/* After the literal there can be field only*/
|
|
Packit |
1422b7 |
if (i < es_strlen(rule)) {
|
|
Packit |
1422b7 |
CHKR(addFieldDescr(ctx, &dag, rule, &i, &str);;
|
|
Packit |
1422b7 |
if (i == es_strlen(rule)) {
|
|
Packit |
1422b7 |
/* finish the tree with empty literal to avoid false merging*/
|
|
Packit |
1422b7 |
CHKR(parseLiteral(ctx, &dag, rule, &i, &str);;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
LN_DBGPRINTF(ctx, "end addSampToTree %zu of %d", i, es_strlen(rule));
|
|
Packit |
1422b7 |
/* we are at the end of rule processing, so this node is a terminal */
|
|
Packit |
1422b7 |
dag->flags.isTerminal = 1;
|
|
Packit |
1422b7 |
dag->tags = tagBucket;
|
|
Packit |
1422b7 |
dag->rb_file = strdup(ctx->conf_file);
|
|
Packit |
1422b7 |
dag->rb_lineno = ctx->conf_ln_nbr;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
if(str != NULL)
|
|
Packit |
1422b7 |
es_deleteStr(str);
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* get the initial word of a rule line that tells us the type of the
|
|
Packit |
1422b7 |
* line.
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[out] offs offset after "="
|
|
Packit |
1422b7 |
* @param[out] str string with "linetype-word" (newly created)
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
getLineType(const char *buf, es_size_t lenBuf, size_t *offs, es_str_t **str)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
size_t i;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
*str = es_newStr(16);
|
|
Packit |
1422b7 |
for(i = 0 ; i < lenBuf && buf[i] != '=' ; ++i) {
|
|
Packit |
1422b7 |
CHKR(es_addChar(str, buf[i]));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(i < lenBuf)
|
|
Packit |
1422b7 |
++i; /* skip over '=' */
|
|
Packit |
1422b7 |
*offs = i;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Get a new common prefix from the config file. That is actually everything from
|
|
Packit |
1422b7 |
* the current offset to the end of line.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in] offs offset after "="
|
|
Packit |
1422b7 |
* @param[in/out] str string to store common offset. If NULL, it is created,
|
|
Packit |
1422b7 |
* otherwise it is emptied.
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
getPrefix(const char *buf, es_size_t lenBuf, es_size_t offs, es_str_t **str)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(*str == NULL) {
|
|
Packit |
1422b7 |
CHKN(*str = es_newStr(lenBuf - offs));
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
es_emptyStr(*str);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
r = es_addBuf(str, (char*)buf + offs, lenBuf - offs);
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Extend the common prefix. This means that the line is concatenated
|
|
Packit |
1422b7 |
* to the prefix. This is useful if the same rulebase is to be used with
|
|
Packit |
1422b7 |
* different prefixes (well, not strictly necessary, but probably useful).
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in] offs offset to-be-added text starts
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
extendPrefix(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t offs)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
return es_addBuf(&ctx->rulePrefix, (char*)buf+offs, lenBuf - offs);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Add a tag to the tag bucket. Helper to processTags.
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] tagname string with tag name
|
|
Packit |
1422b7 |
* @param[out] tagBucket tagbucket to which new tags shall be added
|
|
Packit |
1422b7 |
* the tagbucket is created if it is NULL
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
addTagStrToBucket(ln_ctx ctx, es_str_t *tagname, struct json_object **tagBucket)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
char *cstr;
|
|
Packit |
1422b7 |
struct json_object *tag;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(*tagBucket == NULL) {
|
|
Packit |
1422b7 |
CHKN(*tagBucket = json_object_new_array());
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
cstr = es_str2cstr(tagname, NULL);
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "tag found: '%s'", cstr);
|
|
Packit |
1422b7 |
CHKN(tag = json_object_new_string(cstr));
|
|
Packit |
1422b7 |
json_object_array_add(*tagBucket, tag);
|
|
Packit |
1422b7 |
free(cstr);
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Extract the tags and create a tag bucket out of them
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in,out] poffs offset where tags start, on exit and success
|
|
Packit |
1422b7 |
* offset after tag part (excluding ':')
|
|
Packit |
1422b7 |
* @param[out] tagBucket tagbucket to which new tags shall be added
|
|
Packit |
1422b7 |
* the tagbucket is created if it is NULL
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
processTags(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t *poffs, struct json_object **tagBucket)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
es_str_t *str = NULL;
|
|
Packit |
1422b7 |
es_size_t i;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
assert(poffs != NULL);
|
|
Packit |
1422b7 |
i = *poffs;
|
|
Packit |
1422b7 |
while(i < lenBuf && buf[i] != ':') {
|
|
Packit |
1422b7 |
if(buf[i] == ',') {
|
|
Packit |
1422b7 |
/* end of this tag */
|
|
Packit |
1422b7 |
CHKR(addTagStrToBucket(ctx, str, tagBucket));
|
|
Packit |
1422b7 |
es_deleteStr(str);
|
|
Packit |
1422b7 |
str = NULL;
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
if(str == NULL) {
|
|
Packit |
1422b7 |
CHKN(str = es_newStr(32));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
CHKR(es_addChar(&str, buf[i]));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
++i;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(buf[i] != ':')
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
++i; /* skip ':' */
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(str != NULL) {
|
|
Packit |
1422b7 |
CHKR(addTagStrToBucket(ctx, str, tagBucket));
|
|
Packit |
1422b7 |
es_deleteStr(str);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
*poffs = i;
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Process a new rule and add it to pdag.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in] offs offset where rule starts
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
processRule(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t offs)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
es_str_t *str;
|
|
Packit |
1422b7 |
struct json_object *tagBucket = NULL;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "rule line to add: '%s'", buf+offs);
|
|
Packit |
1422b7 |
CHKR(processTags(ctx, buf, lenBuf, &offs, &tagBucket));
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(offs == lenBuf) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "error: actual message sample part is missing");
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if(ctx->rulePrefix == NULL) {
|
|
Packit |
1422b7 |
CHKN(str = es_newStr(lenBuf));
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
CHKN(str = es_strdup(ctx->rulePrefix));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
CHKR(es_addBuf(&str, (char*)buf + offs, lenBuf - offs));
|
|
Packit |
1422b7 |
addSampToTree(ctx, str, ctx->pdag, tagBucket);
|
|
Packit |
1422b7 |
es_deleteStr(str);
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
getTypeName(ln_ctx ctx,
|
|
Packit |
1422b7 |
const char *const __restrict__ buf,
|
|
Packit |
1422b7 |
const size_t lenBuf,
|
|
Packit |
1422b7 |
size_t *const __restrict__ offs,
|
|
Packit |
1422b7 |
char *const __restrict__ dstbuf)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
size_t iDst;
|
|
Packit |
1422b7 |
size_t i = *offs;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(buf[i] != '@') {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "user-defined type name must "
|
|
Packit |
1422b7 |
"start with '@'");
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
for( iDst = 0
|
|
Packit |
1422b7 |
; i < lenBuf && buf[i] != ':' && iDst < MAX_TYPENAME_LEN - 1
|
|
Packit |
1422b7 |
; ++i, ++iDst) {
|
|
Packit |
1422b7 |
if(isspace(buf[i])) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "user-defined type name must "
|
|
Packit |
1422b7 |
"not contain whitespace");
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
dstbuf[iDst] = buf[i];
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
dstbuf[iDst] = '\0';
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(i < lenBuf && buf[i] == ':') {
|
|
Packit |
1422b7 |
r = 0,
|
|
Packit |
1422b7 |
*offs = i+1; /* skip ":" */
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Process a type definition and add it to the PDAG
|
|
Packit |
1422b7 |
* disconnected components.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in] offs offset where rule starts
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
processType(ln_ctx ctx,
|
|
Packit |
1422b7 |
const char *const __restrict__ buf,
|
|
Packit |
1422b7 |
const size_t lenBuf,
|
|
Packit |
1422b7 |
size_t offs)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
es_str_t *str;
|
|
Packit |
1422b7 |
char typename[MAX_TYPENAME_LEN];
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "type line to add: '%s'", buf+offs);
|
|
Packit |
1422b7 |
CHKR(getTypeName(ctx, buf, lenBuf, &offs, typename));
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "type name is '%s'", typename);
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "type line to add: '%s'", buf+offs);
|
|
Packit |
1422b7 |
if(offs == lenBuf) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "error: actual message sample part is missing in type def");
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
// TODO: optimize
|
|
Packit |
1422b7 |
CHKN(str = es_newStr(lenBuf));
|
|
Packit |
1422b7 |
CHKR(es_addBuf(&str, (char*)buf + offs, lenBuf - offs));
|
|
Packit |
1422b7 |
struct ln_type_pdag *const td = ln_pdagFindType(ctx, typename, 1);
|
|
Packit |
1422b7 |
CHKN(td);
|
|
Packit |
1422b7 |
addSampToTree(ctx, str, td->pdag, NULL);
|
|
Packit |
1422b7 |
es_deleteStr(str);
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Obtain a field name from a rule base line.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in/out] offs on entry: offset where tag starts,
|
|
Packit |
1422b7 |
* on exit: updated offset AFTER TAG and (':')
|
|
Packit |
1422b7 |
* @param [out] strTag obtained tag, if successful
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
getFieldName(ln_ctx __attribute__((unused)) ctx, const char *buf, es_size_t lenBuf, es_size_t *offs,
|
|
Packit |
1422b7 |
es_str_t **strTag)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
es_size_t i;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
i = *offs;
|
|
Packit |
1422b7 |
while(i < lenBuf &&
|
|
Packit |
1422b7 |
(isalnum(buf[i]) || buf[i] == '_' || buf[i] == '.')) {
|
|
Packit |
1422b7 |
if(*strTag == NULL) {
|
|
Packit |
1422b7 |
CHKN(*strTag = es_newStr(32));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
CHKR(es_addChar(strTag, buf[i]));
|
|
Packit |
1422b7 |
++i;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
*offs = i;
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Skip over whitespace.
|
|
Packit |
1422b7 |
* Skips any whitespace present at the offset.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in/out] offs on entry: offset first unprocessed position
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static void
|
|
Packit |
1422b7 |
skipWhitespace(ln_ctx __attribute__((unused)) ctx, const char *buf, es_size_t lenBuf, es_size_t *offs)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
while(*offs < lenBuf && isspace(buf[*offs])) {
|
|
Packit |
1422b7 |
(*offs)++;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Obtain an annotation (field) operation.
|
|
Packit |
1422b7 |
* This usually is a plus or minus sign followed by a field name
|
|
Packit |
1422b7 |
* followed (if plus) by an equal sign and the field value. On entry,
|
|
Packit |
1422b7 |
* offs must be positioned on the first unprocessed field (after ':' for
|
|
Packit |
1422b7 |
* the initial field!). Extra whitespace is detected and, if present,
|
|
Packit |
1422b7 |
* skipped. The obtained operation is added to the annotation set provided.
|
|
Packit |
1422b7 |
* Note that extracted string objects are passed to the annotation; thus it
|
|
Packit |
1422b7 |
* is vital NOT to free them (most importantly, this is *not* a memory leak).
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] annot active annotation set to which the operation is to be added
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in/out] offs on entry: offset where tag starts,
|
|
Packit |
1422b7 |
* on exit: updated offset AFTER TAG and (':')
|
|
Packit |
1422b7 |
* @param [out] strTag obtained tag, if successful
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
getAnnotationOp(ln_ctx ctx, ln_annot *annot, const char *buf, es_size_t lenBuf, es_size_t *offs)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
es_size_t i;
|
|
Packit |
1422b7 |
es_str_t *fieldName = NULL;
|
|
Packit |
1422b7 |
es_str_t *fieldVal = NULL;
|
|
Packit |
1422b7 |
ln_annot_opcode opc;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
i = *offs;
|
|
Packit |
1422b7 |
skipWhitespace(ctx, buf, lenBuf, &i);
|
|
Packit |
1422b7 |
if(i == lenBuf) {
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
goto done; /* nothing left to process (no error!) */
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
switch(buf[i]) {
|
|
Packit |
1422b7 |
case '+':
|
|
Packit |
1422b7 |
opc = ln_annot_ADD;
|
|
Packit |
1422b7 |
break;
|
|
Packit |
1422b7 |
case '#':
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "inline comment in 'annotate' line: %s", buf);
|
|
Packit |
1422b7 |
*offs = lenBuf;
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
case '-':
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "annotate op '-' not yet implemented - failing");
|
|
Packit |
1422b7 |
/*FALLTHROUGH*/
|
|
Packit |
1422b7 |
default:ln_errprintf(ctx, 0, "invalid annotate operation '%c': %s", buf[i], buf+i);
|
|
Packit |
1422b7 |
goto fail;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
i++;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(i == lenBuf) goto fail; /* nothing left to process */
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
CHKR(getFieldName(ctx, buf, lenBuf, &i, &fieldName));
|
|
Packit |
1422b7 |
if(i == lenBuf) goto fail; /* nothing left to process */
|
|
Packit |
1422b7 |
if(buf[i] != '=') goto fail; /* format error */
|
|
Packit |
1422b7 |
i++;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
skipWhitespace(ctx, buf, lenBuf, &i);
|
|
Packit |
1422b7 |
if(buf[i] != '"') goto fail; /* format error */
|
|
Packit |
1422b7 |
++i;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
while(i < lenBuf && buf[i] != '"') {
|
|
Packit |
1422b7 |
if(fieldVal == NULL) {
|
|
Packit |
1422b7 |
CHKN(fieldVal = es_newStr(32));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
CHKR(es_addChar(&fieldVal, buf[i]));
|
|
Packit |
1422b7 |
++i;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
*offs = (i == lenBuf) ? i : i+1;
|
|
Packit |
1422b7 |
CHKR(ln_addAnnotOp(annot, opc, fieldName, fieldVal));
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
fail: return -1;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Process a new annotation and add it to the annotation set.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer
|
|
Packit |
1422b7 |
* @param[in] len length of buffer
|
|
Packit |
1422b7 |
* @param[in] offs offset where annotation starts
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
processAnnotate(ln_ctx ctx, const char *buf, es_size_t lenBuf, es_size_t offs)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r;
|
|
Packit |
1422b7 |
es_str_t *tag = NULL;
|
|
Packit |
1422b7 |
ln_annot *annot;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "sample annotation to add: '%s'", buf+offs);
|
|
Packit |
1422b7 |
CHKR(getFieldName(ctx, buf, lenBuf, &offs, &tag));
|
|
Packit |
1422b7 |
skipWhitespace(ctx, buf, lenBuf, &offs);
|
|
Packit |
1422b7 |
if(buf[offs] != ':' || tag == NULL) {
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "invalid tag field in annotation, line is '%s'", buf);
|
|
Packit |
1422b7 |
r=-1;
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
++offs;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* we got an annotation! */
|
|
Packit |
1422b7 |
CHKN(annot = ln_newAnnot(tag));
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
while(offs < lenBuf) {
|
|
Packit |
1422b7 |
CHKR(getAnnotationOp(ctx, annot, buf, lenBuf, &offs));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
r = ln_addAnnotToSet(ctx->pas, annot);
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done: return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Process include directive. This permits to add unlimited layers
|
|
Packit |
1422b7 |
* of include files.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current context
|
|
Packit |
1422b7 |
* @param[in] buf line buffer, a C-string
|
|
Packit |
1422b7 |
* @param[in] offs offset where annotation starts
|
|
Packit |
1422b7 |
* @returns 0 on success, something else otherwise
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
processInclude(ln_ctx ctx, const char *buf, const size_t offs)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r;
|
|
Packit |
1422b7 |
const char *const conf_file_save = ctx->conf_file;
|
|
Packit |
1422b7 |
char *const fname = strdup(buf+offs);
|
|
Packit |
1422b7 |
size_t lenfname = strlen(fname);
|
|
Packit |
1422b7 |
const unsigned conf_ln_nbr_save = ctx->conf_ln_nbr;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* trim string - not optimized but also no need to */
|
|
Packit |
1422b7 |
for(size_t i = lenfname - 1 ; i > 0 ; --i) {
|
|
Packit |
1422b7 |
if(isspace(fname[i])) {
|
|
Packit |
1422b7 |
fname[i] = '\0';
|
|
Packit |
1422b7 |
--lenfname;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
CHKR(ln_loadSamples(ctx, fname));
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
free(fname);
|
|
Packit |
1422b7 |
ctx->conf_file = conf_file_save;
|
|
Packit |
1422b7 |
ctx->conf_ln_nbr = conf_ln_nbr_save;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Reads a rule (sample) stored in buffer buf and creates a new ln_samp object
|
|
Packit |
1422b7 |
* out of it, which it adds to the pdag (if required).
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[ctx] ctx current library context
|
|
Packit |
1422b7 |
* @param[buf] cstr buffer containing the string contents of the sample
|
|
Packit |
1422b7 |
* @param[lenBuf] length of the sample contained within buf
|
|
Packit |
1422b7 |
* @return standard error code
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
ln_processSamp(ln_ctx ctx, const char *buf, const size_t lenBuf)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 0;
|
|
Packit |
1422b7 |
es_str_t *typeStr = NULL;
|
|
Packit |
1422b7 |
size_t offs;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(getLineType(buf, lenBuf, &offs, &typeStr) != 0)
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(!es_strconstcmp(typeStr, "prefix")) {
|
|
Packit |
1422b7 |
if(getPrefix(buf, lenBuf, offs, &ctx->rulePrefix) != 0) goto done;
|
|
Packit |
1422b7 |
} else if(!es_strconstcmp(typeStr, "extendprefix")) {
|
|
Packit |
1422b7 |
if(extendPrefix(ctx, buf, lenBuf, offs) != 0) goto done;
|
|
Packit |
1422b7 |
} else if(!es_strconstcmp(typeStr, "rule")) {
|
|
Packit |
1422b7 |
if(processRule(ctx, buf, lenBuf, offs) != 0) goto done;
|
|
Packit |
1422b7 |
} else if(!es_strconstcmp(typeStr, "type")) {
|
|
Packit |
1422b7 |
if(processType(ctx, buf, lenBuf, offs) != 0) goto done;
|
|
Packit |
1422b7 |
} else if(!es_strconstcmp(typeStr, "annotate")) {
|
|
Packit |
1422b7 |
if(processAnnotate(ctx, buf, lenBuf, offs) != 0) goto done;
|
|
Packit |
1422b7 |
} else if(!es_strconstcmp(typeStr, "include")) {
|
|
Packit |
1422b7 |
CHKR(processInclude(ctx, buf, offs));
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
char *str;
|
|
Packit |
1422b7 |
str = es_str2cstr(typeStr, NULL);
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "invalid record type detected: '%s'", str);
|
|
Packit |
1422b7 |
free(str);
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
if(typeStr != NULL)
|
|
Packit |
1422b7 |
es_deleteStr(typeStr);
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Read a character from our sample source.
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
ln_sampReadChar(const ln_ctx ctx, FILE *const __restrict__ repo, const char **inpbuf)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int c;
|
|
Packit |
1422b7 |
assert((repo != NULL && inpbuf == NULL) || (repo == NULL && inpbuf != NULL));
|
|
Packit |
1422b7 |
if(repo == NULL) {
|
|
Packit |
1422b7 |
c = (**inpbuf == '\0') ? EOF : *(*inpbuf)++;
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
c = fgetc(repo);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
return c;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* note: comments are only supported at beginning of line! */
|
|
Packit |
1422b7 |
/* skip to end of line */
|
|
Packit |
1422b7 |
void
|
|
Packit |
1422b7 |
ln_sampSkipCommentLine(ln_ctx ctx, FILE * const __restrict__ repo, const char **inpbuf)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int c;
|
|
Packit |
1422b7 |
do {
|
|
Packit |
1422b7 |
c = ln_sampReadChar(ctx, repo, inpbuf);
|
|
Packit |
1422b7 |
} while(c != EOF && c != '\n');
|
|
Packit |
1422b7 |
++ctx->conf_ln_nbr;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* this checks if in a multi-line rule, the next line seems to be a new
|
|
Packit |
1422b7 |
* rule, which would meand we have some unmatched percent signs inside
|
|
Packit |
1422b7 |
* our rule (what we call a "runaway rule"). This can easily happen and
|
|
Packit |
1422b7 |
* is otherwise hard to debug, so let's see if it is the case...
|
|
Packit |
1422b7 |
* @return 1 if this is a runaway rule, 0 if not
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
int
|
|
Packit |
1422b7 |
ln_sampChkRunawayRule(ln_ctx ctx, FILE *const __restrict__ repo, const char **inpbuf)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 1;
|
|
Packit |
1422b7 |
fpos_t fpos;
|
|
Packit |
1422b7 |
char buf[6];
|
|
Packit |
1422b7 |
int cont = 1;
|
|
Packit |
1422b7 |
int read;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
fgetpos(repo, &fpos);
|
|
Packit |
1422b7 |
while(cont) {
|
|
Packit |
1422b7 |
fpos_t inner_fpos;
|
|
Packit |
1422b7 |
fgetpos(repo, &inner_fpos);
|
|
Packit |
1422b7 |
if((read = fread(buf, sizeof(char), sizeof(buf)-1, repo)) == 0) {
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if(buf[0] == '\n') {
|
|
Packit |
1422b7 |
fsetpos(repo, &inner_fpos);
|
|
Packit |
1422b7 |
if(fread(buf, sizeof(char), 1, repo)) {}; /* skip '\n' */
|
|
Packit |
1422b7 |
continue;
|
|
Packit |
1422b7 |
} else if(buf[0] == '#') {
|
|
Packit |
1422b7 |
fsetpos(repo, &inner_fpos);
|
|
Packit |
1422b7 |
const unsigned conf_ln_nbr_save = ctx->conf_ln_nbr;
|
|
Packit |
1422b7 |
ln_sampSkipCommentLine(ctx, repo, inpbuf);
|
|
Packit |
1422b7 |
ctx->conf_ln_nbr = conf_ln_nbr_save;
|
|
Packit |
1422b7 |
continue;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if(read != 5)
|
|
Packit |
1422b7 |
goto done; /* cannot be a rule= line! */
|
|
Packit |
1422b7 |
cont = 0; /* no comment, so we can decide */
|
|
Packit |
1422b7 |
buf[5] = '\0';
|
|
Packit |
1422b7 |
if(!strncmp(buf, "rule=", 5)) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "line has 'rule=' at begin of line, which "
|
|
Packit |
1422b7 |
"does look like a typo in the previous lines (unmatched "
|
|
Packit |
1422b7 |
"%% character) and is forbidden. If valid, please re-format "
|
|
Packit |
1422b7 |
"the rule to start with other characters. Rule ignored.");
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
fsetpos(repo, &fpos);
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/**
|
|
Packit |
1422b7 |
* Read a rule (sample) from repository (sequentially).
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* Reads a sample starting with the current file position and
|
|
Packit |
1422b7 |
* creates a new ln_samp object out of it, which it adds to the
|
|
Packit |
1422b7 |
* pdag.
|
|
Packit |
1422b7 |
*
|
|
Packit |
1422b7 |
* @param[in] ctx current library context
|
|
Packit |
1422b7 |
* @param[in] repo repository descriptor if file input is desired
|
|
Packit |
1422b7 |
* @param[in/out] ptr to ptr of input buffer; this is used if a string is
|
|
Packit |
1422b7 |
* provided instead of a file. If so, this pointer is advanced
|
|
Packit |
1422b7 |
* as data is consumed.
|
|
Packit |
1422b7 |
* @param[out] isEof must be set to 0 on entry and is switched to 1 if EOF occured.
|
|
Packit |
1422b7 |
* @return standard error code
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
ln_sampRead(ln_ctx ctx, FILE *const __restrict__ repo, const char **inpbuf,
|
|
Packit |
1422b7 |
int *const __restrict__ isEof)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 0;
|
|
Packit |
1422b7 |
char buf[64*1024]; /**< max size of rule - TODO: make configurable */
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
size_t i = 0;
|
|
Packit |
1422b7 |
int inParser = 0;
|
|
Packit |
1422b7 |
int done = 0;
|
|
Packit |
1422b7 |
while(!done) {
|
|
Packit |
1422b7 |
const int c = ln_sampReadChar(ctx, repo, inpbuf);
|
|
Packit |
1422b7 |
if(c == EOF) {
|
|
Packit |
1422b7 |
*isEof = 1;
|
|
Packit |
1422b7 |
if(i == 0)
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
else
|
|
Packit |
1422b7 |
done = 1; /* last line missing LF, still process it! */
|
|
Packit |
1422b7 |
} else if(c == '\n') {
|
|
Packit |
1422b7 |
++ctx->conf_ln_nbr;
|
|
Packit |
1422b7 |
if(inParser) {
|
|
Packit |
1422b7 |
if(ln_sampChkRunawayRule(ctx, repo, inpbuf)) {
|
|
Packit |
1422b7 |
/* ignore previous rule */
|
|
Packit |
1422b7 |
inParser = 0;
|
|
Packit |
1422b7 |
i = 0;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if(!inParser && i != 0)
|
|
Packit |
1422b7 |
done = 1;
|
|
Packit |
1422b7 |
} else if(c == '#' && i == 0) {
|
|
Packit |
1422b7 |
ln_sampSkipCommentLine(ctx, repo, inpbuf);
|
|
Packit |
1422b7 |
i = 0; /* back to beginning */
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
if(c == '%')
|
|
Packit |
1422b7 |
inParser = (inParser) ? 0 : 1;
|
|
Packit |
1422b7 |
buf[i++] = c;
|
|
Packit |
1422b7 |
if(i >= sizeof(buf)) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, 0, "line is too long");
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
buf[i] = '\0';
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "read rulebase line[~%d]: '%s'", ctx->conf_ln_nbr, buf);
|
|
Packit |
1422b7 |
CHKR(ln_processSamp(ctx, buf, i));
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* check rulebase format version. Returns 2 if this is v2 rulebase,
|
|
Packit |
1422b7 |
* 1 for any pre-v2 and -1 if there was a problem reading the file.
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
checkVersion(FILE *const fp)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
char buf[64];
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(fgets(buf, sizeof(buf), fp) == NULL)
|
|
Packit |
1422b7 |
return -1;
|
|
Packit |
1422b7 |
if(!strcmp(buf, "version=2\n")) {
|
|
Packit |
1422b7 |
return 2;
|
|
Packit |
1422b7 |
} else {
|
|
Packit |
1422b7 |
return 1;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* we have a v1 rulebase, so let's do all stuff that we need
|
|
Packit |
1422b7 |
* to make that ole piece of ... work.
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static int
|
|
Packit |
1422b7 |
doOldCruft(ln_ctx ctx, const char *file)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = -1;
|
|
Packit |
1422b7 |
if((ctx->ptree = ln_newPTree(ctx, NULL)) == NULL) {
|
|
Packit |
1422b7 |
free(ctx);
|
|
Packit |
1422b7 |
r = -1;
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
r = ln_v1_loadSamples(ctx, file);
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* try to open a rulebase file. This also tries to see if we need to
|
|
Packit |
1422b7 |
* load it from some pre-configured alternative location.
|
|
Packit |
1422b7 |
* @returns open file pointer or NULL in case of error
|
|
Packit |
1422b7 |
*/
|
|
Packit |
1422b7 |
static FILE *
|
|
Packit |
1422b7 |
tryOpenRBFile(ln_ctx ctx, const char *const file)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
FILE *repo = NULL;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if((repo = fopen(file, "r")) != NULL)
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
const int eno1 = errno;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
const char *const rb_lib = getenv("LIBLOGNORM_RULEBASES");
|
|
Packit |
1422b7 |
if(rb_lib == NULL || *file == '/') {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, eno1, "cannot open rulebase '%s'", file);
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
char *fname = NULL;
|
|
Packit |
1422b7 |
int len;
|
|
Packit |
1422b7 |
len = asprintf(&fname, (rb_lib[strlen(rb_lib)-1] == '/') ? "%s%s" : "%s/%s", rb_lib, file);
|
|
Packit |
1422b7 |
if(len == -1) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, errno, "alloc error: cannot open rulebase '%s'", file);
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if((repo = fopen(fname, "r")) == NULL) {
|
|
Packit |
1422b7 |
const int eno2 = errno;
|
|
Packit |
1422b7 |
ln_errprintf(ctx, eno1, "cannot open rulebase '%s'", file);
|
|
Packit |
1422b7 |
ln_errprintf(ctx, eno2, "also tried to locate %s via "
|
|
Packit |
1422b7 |
"rulebase directory without success. Expanded "
|
|
Packit |
1422b7 |
"name was '%s'", file, fname);
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
free(fname);
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
return repo;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* @return 0 if all is ok, 1 if an error occured */
|
|
Packit |
1422b7 |
int
|
|
Packit |
1422b7 |
ln_sampLoad(ln_ctx ctx, const char *file)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 1;
|
|
Packit |
1422b7 |
FILE *repo;
|
|
Packit |
1422b7 |
int isEof = 0;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "loading rulebase file '%s'", file);
|
|
Packit |
1422b7 |
if(file == NULL) goto done;
|
|
Packit |
1422b7 |
if((repo = tryOpenRBFile(ctx, file)) == NULL)
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
const int version = checkVersion(repo);
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "rulebase version is %d\n", version);
|
|
Packit |
1422b7 |
if(version == -1) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, errno, "error determing version of %s", file);
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
if(ctx->version != 0 && version != ctx->version) {
|
|
Packit |
1422b7 |
ln_errprintf(ctx, errno, "rulebase '%s' must be version %d, but is version %d "
|
|
Packit |
1422b7 |
" - can not be processed", file, ctx->version, version);
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
ctx->version = version;
|
|
Packit |
1422b7 |
if(ctx->version == 1) {
|
|
Packit |
1422b7 |
fclose(repo);
|
|
Packit |
1422b7 |
r = doOldCruft(ctx, file);
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* now we are in our native code */
|
|
Packit |
1422b7 |
++ctx->conf_ln_nbr; /* "version=2" is line 1! */
|
|
Packit |
1422b7 |
while(!isEof) {
|
|
Packit |
1422b7 |
CHKR(ln_sampRead(ctx, repo, NULL, &isEof));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
fclose(repo);
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(ctx->include_level == 1)
|
|
Packit |
1422b7 |
ln_pdagOptimize(ctx);
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
/* @return 0 if all is ok, 1 if an error occured */
|
|
Packit |
1422b7 |
int
|
|
Packit |
1422b7 |
ln_sampLoadFromString(ln_ctx ctx, const char *string)
|
|
Packit |
1422b7 |
{
|
|
Packit |
1422b7 |
int r = 1;
|
|
Packit |
1422b7 |
int isEof = 0;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(string == NULL)
|
|
Packit |
1422b7 |
goto done;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
ln_dbgprintf(ctx, "loading v2 rulebase from string '%s'", string);
|
|
Packit |
1422b7 |
ctx->version = 2;
|
|
Packit |
1422b7 |
while(!isEof) {
|
|
Packit |
1422b7 |
CHKR(ln_sampRead(ctx, NULL, &string, &isEof));
|
|
Packit |
1422b7 |
}
|
|
Packit |
1422b7 |
r = 0;
|
|
Packit |
1422b7 |
|
|
Packit |
1422b7 |
if(ctx->include_level == 1)
|
|
Packit |
1422b7 |
ln_pdagOptimize(ctx);
|
|
Packit |
1422b7 |
done:
|
|
Packit |
1422b7 |
return r;
|
|
Packit |
1422b7 |
}
|