Blame src/v1_ptree.c

Packit 1422b7
/**
Packit 1422b7
 * @file ptree.c
Packit 1422b7
 * @brief Implementation of the parse tree object.
Packit 1422b7
 * @class ln_ptree ptree.h
Packit 1422b7
 *//*
Packit 1422b7
 * Copyright 2010 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 <libestr.h>
Packit 1422b7
Packit 1422b7
#define LOGNORM_V1_SUBSYSTEM /* indicate we are old cruft */
Packit 1422b7
#include "v1_liblognorm.h"
Packit 1422b7
#include "annot.h"
Packit 1422b7
#include "internal.h"
Packit 1422b7
#include "lognorm.h"
Packit 1422b7
#include "v1_ptree.h"
Packit 1422b7
#include "v1_samp.h"
Packit 1422b7
#include "v1_parser.h"
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Get base addr of common prefix. Takes length of prefix in account
Packit 1422b7
 * and selects the right buffer.
Packit 1422b7
 */
Packit 1422b7
static inline unsigned char*
Packit 1422b7
prefixBase(struct ln_ptree *tree)
Packit 1422b7
{
Packit 1422b7
	return (tree->lenPrefix <= sizeof(tree->prefix))
Packit 1422b7
	       ? tree->prefix.data : tree->prefix.ptr;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct ln_ptree*
Packit 1422b7
ln_newPTree(ln_ctx ctx, struct ln_ptree **parentptr)
Packit 1422b7
{
Packit 1422b7
	struct ln_ptree *tree;
Packit 1422b7
Packit 1422b7
	if((tree = calloc(1, sizeof(struct ln_ptree))) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
	
Packit 1422b7
	tree->parentptr = parentptr;
Packit 1422b7
	tree->ctx = ctx;
Packit 1422b7
	ctx->nNodes++;
Packit 1422b7
done:	return tree;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_deletePTreeNode(ln_fieldList_t *node)
Packit 1422b7
{
Packit 1422b7
	ln_deletePTree(node->subtree);
Packit 1422b7
	es_deleteStr(node->name);
Packit 1422b7
	if(node->data != NULL)
Packit 1422b7
		es_deleteStr(node->data);
Packit 1422b7
	if(node->raw_data != NULL)
Packit 1422b7
		es_deleteStr(node->raw_data);
Packit 1422b7
	if(node->parser_data != NULL && node->parser_data_destructor != NULL)
Packit 1422b7
		node->parser_data_destructor(&(node->parser_data));
Packit 1422b7
	free(node);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_deletePTree(struct ln_ptree *tree)
Packit 1422b7
{
Packit 1422b7
	ln_fieldList_t *node, *nextnode;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	if(tree == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(tree->tags != NULL)
Packit 1422b7
		json_object_put(tree->tags);
Packit 1422b7
	for(node = tree->froot; node != NULL; node = nextnode) {
Packit 1422b7
		nextnode = node->next;
Packit 1422b7
		ln_deletePTreeNode(node);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* need to free a large prefix buffer? */
Packit 1422b7
	if(tree->lenPrefix > sizeof(tree->prefix))
Packit 1422b7
		free(tree->prefix.ptr);
Packit 1422b7
Packit 1422b7
	for(i = 0 ; i < 256 ; ++i)
Packit 1422b7
		if(tree->subtree[i] != NULL)
Packit 1422b7
			ln_deletePTree(tree->subtree[i]);
Packit 1422b7
	free(tree);
Packit 1422b7
Packit 1422b7
done:	return;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Set the common prefix inside a note, taking into account the subtle
Packit 1422b7
 * issues associated with it.
Packit 1422b7
 * @return 0 on success, something else otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
setPrefix(struct ln_ptree *tree, unsigned char *buf, size_t lenBuf, size_t offs)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "setPrefix lenBuf %zu, offs %zu", lenBuf, offs);
Packit 1422b7
	tree->lenPrefix = lenBuf - offs;
Packit 1422b7
	if(tree->lenPrefix > sizeof(tree->prefix)) {
Packit 1422b7
		/* too-large for standard buffer, need to alloc one */
Packit 1422b7
		if((tree->prefix.ptr = malloc(tree->lenPrefix * sizeof(unsigned char))) == NULL) {
Packit 1422b7
			r = LN_NOMEM;
Packit 1422b7
			goto done; /* fail! */
Packit 1422b7
		}
Packit 1422b7
		memcpy(tree->prefix.ptr, buf, tree->lenPrefix);
Packit 1422b7
	} else {
Packit 1422b7
		/* note: r->lenPrefix may be 0, but that is OK */
Packit 1422b7
		memcpy(tree->prefix.data, buf, tree->lenPrefix);
Packit 1422b7
	}
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Check if the provided tree is a leaf. This means that it
Packit 1422b7
 * does not contain any subtrees.
Packit 1422b7
 * @return 1 if it is a leaf, 0 otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
isLeaf(struct ln_ptree *tree)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	int i;
Packit 1422b7
Packit 1422b7
	if(tree->froot != NULL)
Packit 1422b7
		goto done;
Packit 1422b7
	
Packit 1422b7
	for(i = 0 ; i < 256 ; ++i) {
Packit 1422b7
		if(tree->subtree[i] != NULL)
Packit 1422b7
			goto done;
Packit 1422b7
	}
Packit 1422b7
	r = 1;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Check if the provided tree is a true leaf. This means that it
Packit 1422b7
 * does not contain any subtrees of any kind and no prefix,
Packit 1422b7
 * and it is not terminal leaf.
Packit 1422b7
 * @return 1 if it is a leaf, 0 otherwise
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
isTrueLeaf(struct ln_ptree *tree)
Packit 1422b7
{
Packit 1422b7
	return((tree->lenPrefix == 0) && isLeaf(tree)) && !tree->flags.isTerminal;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct ln_ptree *
Packit 1422b7
ln_addPTree(struct ln_ptree *tree, es_str_t *str, size_t offs)
Packit 1422b7
{
Packit 1422b7
	struct ln_ptree *r;
Packit 1422b7
	struct ln_ptree **parentptr;	 /**< pointer in parent that needs to be updated */
Packit 1422b7
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "addPTree: offs %zu", offs);
Packit 1422b7
	parentptr = &(tree->subtree[es_getBufAddr(str)[offs]]);
Packit 1422b7
	/* First check if tree node is totaly empty. If so, we can simply add
Packit 1422b7
	 * the prefix to this node. This case is important, because it happens
Packit 1422b7
	 * every time with a new field.
Packit 1422b7
	 */
Packit 1422b7
	if(isTrueLeaf(tree)) {
Packit 1422b7
		if(setPrefix(tree, es_getBufAddr(str), es_strlen(str), offs) != 0) {
Packit 1422b7
			r = NULL;
Packit 1422b7
		} else {
Packit 1422b7
			r = tree;
Packit 1422b7
		}
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(tree->ctx->debug) {
Packit 1422b7
		char *cstr = es_str2cstr(str, NULL);
Packit 1422b7
		LN_DBGPRINTF(tree->ctx, "addPTree: add '%s', offs %zu, tree %p",
Packit 1422b7
			     cstr + offs, offs, tree);
Packit 1422b7
		free(cstr);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if((r = ln_newPTree(tree->ctx, parentptr)) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(setPrefix(r, es_getBufAddr(str) + offs + 1, es_strlen(str) - offs - 1, 0) != 0) {
Packit 1422b7
		free(r);
Packit 1422b7
		r = NULL;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	*parentptr = r;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Split the provided tree (node) into two at the provided index into its
Packit 1422b7
 * common prefix. This function exists to support splitting nodes when
Packit 1422b7
 * a mismatch in the common prefix requires that. This function more or less
Packit 1422b7
 * keeps the tree as it is, just changes the structure. No new node is added.
Packit 1422b7
 * Usually, it is desired to add a new node. This must be made afterwards.
Packit 1422b7
 * Note that we need to create a new tree *in front of* the current one, as
Packit 1422b7
 * the current one contains field etc. subtree pointers.
Packit 1422b7
 * @param[in] tree tree to split
Packit 1422b7
 * @param[in] offs offset into common prefix (must be less than prefix length!)
Packit 1422b7
 */
Packit 1422b7
static struct ln_ptree*
Packit 1422b7
splitTree(struct ln_ptree *tree, unsigned short offs)
Packit 1422b7
{
Packit 1422b7
	unsigned char *c;
Packit 1422b7
	struct ln_ptree *r;
Packit 1422b7
	unsigned short newlen;
Packit 1422b7
	ln_ptree **newparentptr;	 /**< pointer in parent that needs to be updated */
Packit 1422b7
Packit 1422b7
	assert(offs < tree->lenPrefix);
Packit 1422b7
	if((r = ln_newPTree(tree->ctx, tree->parentptr)) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(tree->ctx, "splitTree %p at offs %u", tree, offs);
Packit 1422b7
	/* note: the overall prefix is reduced by one char, which is now taken
Packit 1422b7
	 * care of inside the "branch table".
Packit 1422b7
	 */
Packit 1422b7
	c = prefixBase(tree);
Packit 1422b7
//LN_DBGPRINTF(tree->ctx, "splitTree new bb, *(c+offs): '%s'", c);
Packit 1422b7
	if(setPrefix(r, c, offs, 0) != 0) {
Packit 1422b7
		ln_deletePTree(r);
Packit 1422b7
		r = NULL;
Packit 1422b7
		goto done; /* fail! */
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "splitTree new tree %p lenPrefix=%u, char '%c'", r, r->lenPrefix, r->prefix.data[0]);
Packit 1422b7
	/* add the proper branch table entry for the new node. must be done
Packit 1422b7
	 * here, because the next step will destroy the required index char!
Packit 1422b7
	 */
Packit 1422b7
	newparentptr = &(r->subtree[c[offs]]);
Packit 1422b7
	r->subtree[c[offs]] = tree;
Packit 1422b7
Packit 1422b7
	/* finally fix existing common prefix */
Packit 1422b7
	newlen = tree->lenPrefix - offs - 1;
Packit 1422b7
	if(tree->lenPrefix > sizeof(tree->prefix) && (newlen <= sizeof(tree->prefix))) {
Packit 1422b7
		/* note: c is a different pointer; the original
Packit 1422b7
		 * pointer is overwritten by memcpy! */
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "splitTree new case one bb, offs %u, lenPrefix %u, newlen %u", offs, tree->lenPrefix, newlen);
Packit 1422b7
//LN_DBGPRINTF(tree->ctx, "splitTree new case one bb, *(c+offs): '%s'", c);
Packit 1422b7
		memcpy(tree->prefix.data, c+offs+1, newlen);
Packit 1422b7
		free(c);
Packit 1422b7
	} else {
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "splitTree new case two bb, offs=%u, newlen %u", offs, newlen);
Packit 1422b7
		memmove(c, c+offs+1, newlen);
Packit 1422b7
	}
Packit 1422b7
	tree->lenPrefix = tree->lenPrefix - offs - 1;
Packit 1422b7
Packit 1422b7
	if(tree->parentptr == 0)
Packit 1422b7
		tree->ctx->ptree = r;	/* root does not have a parent! */
Packit 1422b7
	else
Packit 1422b7
		*(tree->parentptr) = r;
Packit 1422b7
	tree->parentptr = newparentptr;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct ln_ptree *
Packit 1422b7
ln_buildPTree(struct ln_ptree *tree, es_str_t *str, size_t offs)
Packit 1422b7
{
Packit 1422b7
	struct ln_ptree *r;
Packit 1422b7
	unsigned char *c;
Packit 1422b7
	unsigned char *cpfix;
Packit 1422b7
	size_t i;
Packit 1422b7
	unsigned short ipfix;
Packit 1422b7
Packit 1422b7
	assert(tree != NULL);
Packit 1422b7
	LN_DBGPRINTF(tree->ctx, "buildPTree: begin at %p, offs %zu", tree, offs);
Packit 1422b7
	c = es_getBufAddr(str);
Packit 1422b7
Packit 1422b7
	/* check if the prefix matches and, if not, at what offset it is different */
Packit 1422b7
	ipfix = 0;
Packit 1422b7
	cpfix = prefixBase(tree);
Packit 1422b7
	for(  i = offs
Packit 1422b7
	    ; (i < es_strlen(str)) && (ipfix < tree->lenPrefix) && (c[i] == cpfix[ipfix])
Packit 1422b7
	    ; ++i, ++ipfix) {
Packit 1422b7
	    	; /*DO NOTHING - just find end of match */
Packit 1422b7
		LN_DBGPRINTF(tree->ctx, "buildPTree: tree %p, i %zu, char '%c'", tree, i, c[i]);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* if we reach this point, we have processed as much of the common prefix
Packit 1422b7
	 * as we could. The following code now does the proper actions based on
Packit 1422b7
	 * the possible cases.
Packit 1422b7
	 */
Packit 1422b7
	if(i == es_strlen(str)) {
Packit 1422b7
		/* all of our input is consumed, no more recursion */
Packit 1422b7
		if(ipfix == tree->lenPrefix) {
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "case 1.1");
Packit 1422b7
			/* exact match, we are done! */
Packit 1422b7
			r = tree;
Packit 1422b7
		} else {
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "case 1.2");
Packit 1422b7
			/* we need to split the node at the current position */
Packit 1422b7
			r = splitTree(tree, ipfix);
Packit 1422b7
		}
Packit 1422b7
	} else if(ipfix < tree->lenPrefix) {
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "case 2, i=%zu, ipfix=%u", i, ipfix);
Packit 1422b7
			/* we need to split the node at the current position */
Packit 1422b7
			if((r = splitTree(tree, ipfix)) == NULL)
Packit 1422b7
				goto done; /* fail */
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "pre addPTree: i %zu", i);
Packit 1422b7
			if((r = ln_addPTree(r, str, i)) == NULL)
Packit 1422b7
				goto done;
Packit 1422b7
			//r = ln_buildPTree(r, str, i + 1);
Packit 1422b7
	} else {
Packit 1422b7
		/* we could consume the current common prefix, but now need
Packit 1422b7
		 * to traverse the rest of the tree based on the next char.
Packit 1422b7
		 */
Packit 1422b7
		if(tree->subtree[c[i]] == NULL) {
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "case 3.1");
Packit 1422b7
			/* non-match, need new subtree */
Packit 1422b7
			r = ln_addPTree(tree, str, i);
Packit 1422b7
		} else {
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "case 3.2");
Packit 1422b7
			/* match, follow subtree */
Packit 1422b7
			r = ln_buildPTree(tree->subtree[c[i]], str, i + 1);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
//LN_DBGPRINTF(tree->ctx, "---------------------------------------");
Packit 1422b7
//ln_displayPTree(tree, 0);
Packit 1422b7
//LN_DBGPRINTF(tree->ctx, "=======================================");
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
int
Packit 1422b7
ln_addFDescrToPTree(struct ln_ptree **tree, ln_fieldList_t *node)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	ln_fieldList_t *curr;
Packit 1422b7
Packit 1422b7
	assert(tree != NULL);assert(*tree != NULL);
Packit 1422b7
	assert(node != NULL);
Packit 1422b7
Packit 1422b7
	if((node->subtree = ln_newPTree((*tree)->ctx, &node->subtree)) == NULL) {
Packit 1422b7
		r = -1;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	LN_DBGPRINTF((*tree)->ctx, "got new subtree %p", node->subtree);
Packit 1422b7
Packit 1422b7
	/* check if we already have this field, if so, merge
Packit 1422b7
	 * TODO: optimized, check logic
Packit 1422b7
	 */
Packit 1422b7
	for(curr = (*tree)->froot ; curr != NULL ; curr = curr->next) {
Packit 1422b7
		if(!es_strcmp(curr->name, node->name)
Packit 1422b7
				&& curr->parser == node->parser
Packit 1422b7
				&& ((curr->raw_data == NULL && node->raw_data == NULL)
Packit 1422b7
					|| (curr->raw_data != NULL && node->raw_data != NULL
Packit 1422b7
						&& !es_strcmp(curr->raw_data, node->raw_data)))) {
Packit 1422b7
			*tree = curr->subtree;
Packit 1422b7
			ln_deletePTreeNode(node);
Packit 1422b7
			r = 0;
Packit 1422b7
			LN_DBGPRINTF((*tree)->ctx, "merging with tree %p\n", *tree);
Packit 1422b7
			goto done;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if((*tree)->froot == NULL) {
Packit 1422b7
		(*tree)->froot = (*tree)->ftail = node;
Packit 1422b7
	} else {
Packit 1422b7
		(*tree)->ftail->next = node;
Packit 1422b7
		(*tree)->ftail = node;
Packit 1422b7
	}
Packit 1422b7
	r = 0;
Packit 1422b7
	LN_DBGPRINTF((*tree)->ctx, "prev subtree %p", *tree);
Packit 1422b7
	*tree = node->subtree;
Packit 1422b7
	LN_DBGPRINTF((*tree)->ctx, "new subtree %p", *tree);
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_displayPTree(struct ln_ptree *tree, int level)
Packit 1422b7
{
Packit 1422b7
	int i;
Packit 1422b7
	int nChildLit;
Packit 1422b7
	int nChildField;
Packit 1422b7
	es_str_t *str;
Packit 1422b7
	char *cstr;
Packit 1422b7
	ln_fieldList_t *node;
Packit 1422b7
	char indent[2048];
Packit 1422b7
Packit 1422b7
	if(level > 1023)
Packit 1422b7
		level = 1023;
Packit 1422b7
	memset(indent, ' ', level * 2);
Packit 1422b7
	indent[level * 2] = '\0';
Packit 1422b7
Packit 1422b7
	nChildField = 0;
Packit 1422b7
	for(node = tree->froot ; node != NULL ; node = node->next ) {
Packit 1422b7
		++nChildField;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	nChildLit = 0;
Packit 1422b7
	for(i = 0 ; i < 256 ; ++i) {
Packit 1422b7
		if(tree->subtree[i] != NULL) {
Packit 1422b7
			nChildLit++;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	str = es_newStr(sizeof(tree->prefix));
Packit 1422b7
	es_addBuf(&str, (char*) prefixBase(tree), tree->lenPrefix);
Packit 1422b7
	cstr = es_str2cstr(str, NULL);
Packit 1422b7
	es_deleteStr(str);
Packit 1422b7
	LN_DBGPRINTF(tree->ctx, "%ssubtree%s %p (prefix: '%s', children: %d literals, %d fields) [visited %u "
Packit 1422b7
	"backtracked %u terminated %u]",
Packit 1422b7
			indent, tree->flags.isTerminal ? " TERM" : "", tree, cstr, nChildLit, nChildField,
Packit 1422b7
			tree->stats.visited, tree->stats.backtracked, tree->stats.terminated);
Packit 1422b7
	free(cstr);
Packit 1422b7
	/* display char subtrees */
Packit 1422b7
	for(i = 0 ; i < 256 ; ++i) {
Packit 1422b7
		if(tree->subtree[i] != NULL) {
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "%schar %2.2x(%c):", indent, i, i);
Packit 1422b7
			ln_displayPTree(tree->subtree[i], level + 1);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* display field subtrees */
Packit 1422b7
	for(node = tree->froot ; node != NULL ; node = node->next ) {
Packit 1422b7
		cstr = es_str2cstr(node->name, NULL);
Packit 1422b7
		LN_DBGPRINTF(tree->ctx, "%sfield %s:", indent, cstr);
Packit 1422b7
		free(cstr);
Packit 1422b7
		ln_displayPTree(node->subtree, level + 1);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* the following is a quick hack, which should be moved to the
Packit 1422b7
 * string class.
Packit 1422b7
 */
Packit 1422b7
static inline void dotAddPtr(es_str_t **str, void *p)
Packit 1422b7
{
Packit 1422b7
	char buf[64];
Packit 1422b7
	int i;
Packit 1422b7
	i = snprintf(buf, sizeof(buf), "%p", p);
Packit 1422b7
	es_addBuf(str, buf, i);
Packit 1422b7
}
Packit 1422b7
/**
Packit 1422b7
 * recursive handler for DOT graph generator.
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
ln_genDotPTreeGraphRec(struct ln_ptree *tree, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	int i;
Packit 1422b7
	ln_fieldList_t *node;
Packit 1422b7
Packit 1422b7
Packit 1422b7
	dotAddPtr(str, tree);
Packit 1422b7
	es_addBufConstcstr(str, " [label=\"");
Packit 1422b7
	if(tree->lenPrefix > 0) {
Packit 1422b7
		es_addChar(str, '\'');
Packit 1422b7
		es_addBuf(str, (char*) prefixBase(tree), tree->lenPrefix);
Packit 1422b7
		es_addChar(str, '\'');
Packit 1422b7
	}
Packit 1422b7
	es_addBufConstcstr(str, "\"");
Packit 1422b7
	if(isLeaf(tree)) {
Packit 1422b7
		es_addBufConstcstr(str, " style=\"bold\"");
Packit 1422b7
	}
Packit 1422b7
	es_addBufConstcstr(str, "]\n");
Packit 1422b7
Packit 1422b7
	/* display char subtrees */
Packit 1422b7
	for(i = 0 ; i < 256 ; ++i) {
Packit 1422b7
		if(tree->subtree[i] != NULL) {
Packit 1422b7
			dotAddPtr(str, tree);
Packit 1422b7
			es_addBufConstcstr(str, " -> ");
Packit 1422b7
			dotAddPtr(str, tree->subtree[i]);
Packit 1422b7
			es_addBufConstcstr(str, " [label=\"");
Packit 1422b7
			es_addChar(str, (char) i);
Packit 1422b7
			es_addBufConstcstr(str, "\"]\n");
Packit 1422b7
			ln_genDotPTreeGraphRec(tree->subtree[i], str);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* display field subtrees */
Packit 1422b7
	for(node = tree->froot ; node != NULL ; node = node->next ) {
Packit 1422b7
		dotAddPtr(str, tree);
Packit 1422b7
		es_addBufConstcstr(str, " -> ");
Packit 1422b7
		dotAddPtr(str, node->subtree);
Packit 1422b7
		es_addBufConstcstr(str, " [label=\"");
Packit 1422b7
		es_addStr(str, node->name);
Packit 1422b7
		es_addBufConstcstr(str, "\" style=\"dotted\"]\n");
Packit 1422b7
		ln_genDotPTreeGraphRec(node->subtree, str);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_genDotPTreeGraph(struct ln_ptree *tree, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	es_addBufConstcstr(str, "digraph ptree {\n");
Packit 1422b7
	ln_genDotPTreeGraphRec(tree, str);
Packit 1422b7
	es_addBufConstcstr(str, "}\n");
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * add unparsed string to event.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
addUnparsedField(const char *str, size_t strLen, int offs, struct json_object *json)
Packit 1422b7
{
Packit 1422b7
	int r = 1;
Packit 1422b7
	struct json_object *value;
Packit 1422b7
	char *s = NULL;
Packit 1422b7
	CHKN(s = strndup(str, strLen));
Packit 1422b7
	value = json_object_new_string(s);
Packit 1422b7
	if (value == NULL) {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	json_object_object_add(json, ORIGINAL_MSG_KEY, value);
Packit 1422b7
	
Packit 1422b7
	value = json_object_new_string(s + offs);
Packit 1422b7
	if (value == NULL) {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	json_object_object_add(json, UNPARSED_DATA_KEY, value);
Packit 1422b7
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	free(s);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Special parser for iptables-like name/value pairs.
Packit 1422b7
 * The pull multiple fields. Note that once this parser has been selected,
Packit 1422b7
 * it is very unlikely to be left, as it is *very* generic. This parser is
Packit 1422b7
 * required because practice shows that already-structured data like iptables
Packit 1422b7
 * can otherwise not be processed by liblognorm in a meaningful way.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] tree current tree to process
Packit 1422b7
 * @param[in] str string to be matched against (the to-be-normalized data)
Packit 1422b7
 * @param[in] strLen length of str
Packit 1422b7
 * @param[in/out] offs start position in input data, on exit first unparsed position
Packit 1422b7
 * @param[in/out] event handle to event that is being created during normalization
Packit 1422b7
 *
Packit 1422b7
 * @return 0 if parser was successfully, something else on error
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
ln_iptablesParser(struct ln_ptree *tree, const char *str, size_t strLen, size_t *offs,
Packit 1422b7
		  struct json_object *json)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	size_t o = *offs;
Packit 1422b7
	es_str_t *fname;
Packit 1422b7
	es_str_t *fval;
Packit 1422b7
	const char *pstr;
Packit 1422b7
	const char *end;
Packit 1422b7
	struct json_object *value;
Packit 1422b7
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "%zu enter iptables parser, len %zu", *offs, strLen);
Packit 1422b7
	if(o == strLen) {
Packit 1422b7
		r = -1; /* can not be, we have no n/v pairs! */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	
Packit 1422b7
	end = str + strLen;
Packit 1422b7
	pstr = str + o;
Packit 1422b7
	while(pstr < end) {
Packit 1422b7
		while(pstr < end && isspace(*pstr))
Packit 1422b7
			++pstr;
Packit 1422b7
		CHKN(fname = es_newStr(16));
Packit 1422b7
		while(pstr < end && !isspace(*pstr) && *pstr != '=') {
Packit 1422b7
			es_addChar(&fname, *pstr);
Packit 1422b7
			++pstr;
Packit 1422b7
		}
Packit 1422b7
		if(pstr < end && *pstr == '=') {
Packit 1422b7
			CHKN(fval = es_newStr(16));
Packit 1422b7
			++pstr;
Packit 1422b7
			/* error on space */
Packit 1422b7
			while(pstr < end && !isspace(*pstr)) {
Packit 1422b7
				es_addChar(&fval, *pstr);
Packit 1422b7
				++pstr;
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			CHKN(fval = es_newStrFromCStr("[*PRESENT*]",
Packit 1422b7
					sizeof("[*PRESENT*]")-1));
Packit 1422b7
		}
Packit 1422b7
		char *cn, *cv;
Packit 1422b7
		CHKN(cn = ln_es_str2cstr(&fname));
Packit 1422b7
		CHKN(cv = ln_es_str2cstr(&fval));
Packit 1422b7
		if (tree->ctx->debug) {
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "iptables parser extracts %s=%s", cn, cv);
Packit 1422b7
		}
Packit 1422b7
		CHKN(value = json_object_new_string(cv));
Packit 1422b7
		json_object_object_add(json, cn, value);
Packit 1422b7
		es_deleteStr(fval);
Packit 1422b7
		es_deleteStr(fname);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r = 0;
Packit 1422b7
	*offs = strLen;
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	LN_DBGPRINTF(tree->ctx, "%zu iptables parser returns %d", *offs, r);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Recursive step of the normalizer. It walks the parse tree and calls itself
Packit 1422b7
 * recursively when this is appropriate. It also implements backtracking in
Packit 1422b7
 * those (hopefully rare) cases where it is required.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] tree current tree to process
Packit 1422b7
 * @param[in] string string to be matched against (the to-be-normalized data)
Packit 1422b7
 * @param[in] strLen length of the to-be-matched string
Packit 1422b7
 * @param[in] offs start position in input data
Packit 1422b7
 * @param[in/out] json ... that is being created during normalization
Packit 1422b7
 * @param[out] endNode if a match was found, this is the matching node (undefined otherwise)
Packit 1422b7
 *
Packit 1422b7
 * @return number of characters left unparsed by following the subtree, negative if
Packit 1422b7
 *         the to-be-parsed message is shorter than the rule sample by this number of
Packit 1422b7
 *         characters.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
ln_v1_normalizeRec(struct ln_ptree *tree, const char *str, size_t strLen, size_t offs, struct json_object *json,
Packit 1422b7
		struct ln_ptree **endNode)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	int localR;
Packit 1422b7
	size_t i;
Packit 1422b7
	int left;
Packit 1422b7
	ln_fieldList_t *node;
Packit 1422b7
	ln_fieldList_t *restMotifNode = NULL;
Packit 1422b7
	char *cstr;
Packit 1422b7
	const char *c;
Packit 1422b7
	unsigned char *cpfix;
Packit 1422b7
	unsigned ipfix;
Packit 1422b7
	size_t parsed;
Packit 1422b7
	char *namestr;
Packit 1422b7
	struct json_object *value;
Packit 1422b7
	
Packit 1422b7
	++tree->stats.visited;
Packit 1422b7
	if(offs >= strLen) {
Packit 1422b7
		*endNode = tree;
Packit 1422b7
		r = -tree->lenPrefix;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "%zu: enter parser, tree %p", offs, tree);
Packit 1422b7
	c = str;
Packit 1422b7
	cpfix = prefixBase(tree);
Packit 1422b7
	node = tree->froot;
Packit 1422b7
	r = strLen - offs;
Packit 1422b7
	/* first we need to check if the common prefix matches (and consume input data while we do) */
Packit 1422b7
	ipfix = 0;
Packit 1422b7
	while(offs < strLen && ipfix < tree->lenPrefix) {
Packit 1422b7
		LN_DBGPRINTF(tree->ctx, "%zu: prefix compare '%c', '%c'", offs, c[offs], cpfix[ipfix]);
Packit 1422b7
		if(c[offs] != cpfix[ipfix]) {
Packit 1422b7
			r -= ipfix;
Packit 1422b7
			goto done;
Packit 1422b7
		}
Packit 1422b7
		++offs, ++ipfix;
Packit 1422b7
	}
Packit 1422b7
	
Packit 1422b7
	if(ipfix != tree->lenPrefix) {
Packit 1422b7
		/* incomplete prefix match --> to-be-normalized string too short */
Packit 1422b7
		r = ipfix - tree->lenPrefix;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r -= ipfix;
Packit 1422b7
	LN_DBGPRINTF(tree->ctx, "%zu: prefix compare succeeded, still valid", offs);
Packit 1422b7
Packit 1422b7
	/* now try the parsers */
Packit 1422b7
	while(node != NULL) {
Packit 1422b7
		if(tree->ctx->debug) {
Packit 1422b7
			cstr = es_str2cstr(node->name, NULL);
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "%zu:trying parser for field '%s': %p",
Packit 1422b7
					offs, cstr, node->parser);
Packit 1422b7
			free(cstr);
Packit 1422b7
		}
Packit 1422b7
		i = offs;
Packit 1422b7
		if(node->isIPTables) {
Packit 1422b7
			localR = ln_iptablesParser(tree, str, strLen, &i, json);
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "%zu iptables parser return, i=%zu",
Packit 1422b7
						offs, i);
Packit 1422b7
			if(localR == 0) {
Packit 1422b7
				/* potential hit, need to verify */
Packit 1422b7
				LN_DBGPRINTF(tree->ctx, "potential hit, trying subtree");
Packit 1422b7
				left = ln_v1_normalizeRec(node->subtree, str, strLen, i, json, endNode);
Packit 1422b7
				if(left == 0 && (*endNode)->flags.isTerminal) {
Packit 1422b7
					LN_DBGPRINTF(tree->ctx, "%zu: parser matches at %zu", offs, i);
Packit 1422b7
					r = 0;
Packit 1422b7
					goto done;
Packit 1422b7
				}
Packit 1422b7
				LN_DBGPRINTF(tree->ctx, "%zu nonmatch, backtracking required, left=%d",
Packit 1422b7
						offs, left);
Packit 1422b7
				++tree->stats.backtracked;
Packit 1422b7
				if(left < r)
Packit 1422b7
					r = left;
Packit 1422b7
			}
Packit 1422b7
		} else if(node->parser == ln_parseRest) {
Packit 1422b7
			/* This is a quick and dirty adjustment to handle "rest" more intelligently.
Packit 1422b7
			 * It's just a tactical fix: in the longer term, we'll handle the whole
Packit 1422b7
			 * situation differently. However, it makes sense to fix this now, as this
Packit 1422b7
			 * solves some real-world problems immediately. -- rgerhards, 2015-04-15
Packit 1422b7
			 */
Packit 1422b7
			restMotifNode = node;
Packit 1422b7
		} else {
Packit 1422b7
			value = NULL;
Packit 1422b7
			localR = node->parser(str, strLen, &i, node, &parsed, &value);
Packit 1422b7
			LN_DBGPRINTF(tree->ctx, "parser returns %d, parsed %zu", localR, parsed);
Packit 1422b7
			if(localR == 0) {
Packit 1422b7
				/* potential hit, need to verify */
Packit 1422b7
				LN_DBGPRINTF(tree->ctx, "%zu: potential hit, trying subtree %p", offs, node->subtree);
Packit 1422b7
				left = ln_v1_normalizeRec(node->subtree, str, strLen, i + parsed, json, endNode);
Packit 1422b7
				LN_DBGPRINTF(tree->ctx, "%zu: subtree returns %d", offs, r);
Packit 1422b7
				if(left == 0 && (*endNode)->flags.isTerminal) {
Packit 1422b7
					LN_DBGPRINTF(tree->ctx, "%zu: parser matches at %zu", offs, i);
Packit 1422b7
					if(es_strbufcmp(node->name, (unsigned char*)"-", 1)) {
Packit 1422b7
						/* Store the value here; create json if not already created */
Packit 1422b7
						if (value == NULL) {
Packit 1422b7
							CHKN(cstr = strndup(str + i, parsed));
Packit 1422b7
							value = json_object_new_string(cstr);
Packit 1422b7
							free(cstr);
Packit 1422b7
						}
Packit 1422b7
						if (value == NULL) {
Packit 1422b7
							LN_DBGPRINTF(tree->ctx, "unable to create json");
Packit 1422b7
							goto done;
Packit 1422b7
						}
Packit 1422b7
						namestr = ln_es_str2cstr(&node->name);
Packit 1422b7
						json_object_object_add(json, namestr, value);
Packit 1422b7
					} else {
Packit 1422b7
						if (value != NULL) {
Packit 1422b7
							/* Free the unneeded value */
Packit 1422b7
							json_object_put(value);
Packit 1422b7
						}
Packit 1422b7
					}
Packit 1422b7
					r = 0;
Packit 1422b7
					goto done;
Packit 1422b7
				}
Packit 1422b7
				LN_DBGPRINTF(tree->ctx, "%zu nonmatch, backtracking required, left=%d",
Packit 1422b7
						offs, left);
Packit 1422b7
				if (value != NULL) {
Packit 1422b7
					/* Free the value if it was created */
Packit 1422b7
					json_object_put(value);
Packit 1422b7
				}
Packit 1422b7
				if(left > 0 && left < r)
Packit 1422b7
					r = left;
Packit 1422b7
				LN_DBGPRINTF(tree->ctx, "%zu nonmatch, backtracking required, left=%d, r now %d",
Packit 1422b7
						offs, left, r);
Packit 1422b7
				++tree->stats.backtracked;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		node = node->next;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(offs == strLen) {
Packit 1422b7
		*endNode = tree;
Packit 1422b7
		r = 0;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
if(offs < strLen) {
Packit 1422b7
unsigned char cc = str[offs];
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "%zu no field, trying subtree char '%c': %p", offs, cc, tree->subtree[cc]);
Packit 1422b7
} else {
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "%zu no field, offset already beyond end", offs);
Packit 1422b7
}
Packit 1422b7
	/* now let's see if we have a literal */
Packit 1422b7
	if(tree->subtree[(unsigned char)str[offs]] != NULL) {
Packit 1422b7
		left = ln_v1_normalizeRec(tree->subtree[(unsigned char)str[offs]],
Packit 1422b7
				       str, strLen, offs + 1, json, endNode);
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "%zu got left %d, r %d", offs, left, r);
Packit 1422b7
		if(left < r)
Packit 1422b7
			r = left;
Packit 1422b7
LN_DBGPRINTF(tree->ctx, "%zu got return %d", offs, r);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(r == 0 && (*endNode)->flags.isTerminal)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* and finally give "rest" a try if it was present. Note that we MUST do this after
Packit 1422b7
	 * literal evaluation, otherwise "rest" can never be overriden by other rules.
Packit 1422b7
	 */
Packit 1422b7
	if(restMotifNode != NULL) {
Packit 1422b7
		LN_DBGPRINTF(tree->ctx, "rule has rest motif, forcing match via it");
Packit 1422b7
		value = NULL;
Packit 1422b7
		restMotifNode->parser(str, strLen, &i, restMotifNode, &parsed, &value);
Packit 1422b7
#		ifndef NDEBUG
Packit 1422b7
		left = /* we only need this for the assert below */
Packit 1422b7
#		endif
Packit 1422b7
		       ln_v1_normalizeRec(restMotifNode->subtree, str, strLen, i + parsed, json, endNode);
Packit 1422b7
		assert(left == 0); /* with rest, we have this invariant */
Packit 1422b7
		assert((*endNode)->flags.isTerminal); /* this one also */
Packit 1422b7
		LN_DBGPRINTF(tree->ctx, "%zu: parser matches at %zu", offs, i);
Packit 1422b7
		if(es_strbufcmp(restMotifNode->name, (unsigned char*)"-", 1)) {
Packit 1422b7
			/* Store the value here; create json if not already created */
Packit 1422b7
			if (value == NULL) {
Packit 1422b7
				CHKN(cstr = strndup(str + i, parsed));
Packit 1422b7
				value = json_object_new_string(cstr);
Packit 1422b7
				free(cstr);
Packit 1422b7
			}
Packit 1422b7
			if (value == NULL) {
Packit 1422b7
				LN_DBGPRINTF(tree->ctx, "unable to create json");
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
			namestr = ln_es_str2cstr(&restMotifNode->name);
Packit 1422b7
			json_object_object_add(json, namestr, value);
Packit 1422b7
		} else {
Packit 1422b7
			if (value != NULL) {
Packit 1422b7
				/* Free the unneeded value */
Packit 1422b7
				json_object_put(value);
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		r = 0;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
done:
Packit 1422b7
	LN_DBGPRINTF(tree->ctx, "%zu returns %d", offs, r);
Packit 1422b7
	if(r == 0 && *endNode == tree)
Packit 1422b7
		++tree->stats.terminated;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
int
Packit 1422b7
ln_v1_normalize(ln_ctx ctx, const char *str, size_t strLen, struct json_object **json_p)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	int left;
Packit 1422b7
	struct ln_ptree *endNode = NULL;
Packit 1422b7
Packit 1422b7
	if(*json_p == NULL) {
Packit 1422b7
		CHKN(*json_p = json_object_new_object());
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	left = ln_v1_normalizeRec(ctx->ptree, str, strLen, 0, *json_p, &endNode);
Packit 1422b7
Packit 1422b7
	if(ctx->debug) {
Packit 1422b7
		if(left == 0) {
Packit 1422b7
			LN_DBGPRINTF(ctx, "final result for normalizer: left %d, endNode %p, "
Packit 1422b7
				     "isTerminal %d, tagbucket %p",
Packit 1422b7
				     left, endNode, endNode->flags.isTerminal, endNode->tags);
Packit 1422b7
		} else {
Packit 1422b7
			LN_DBGPRINTF(ctx, "final result for normalizer: left %d, endNode %p",
Packit 1422b7
				     left, endNode);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	if(left != 0 || !endNode->flags.isTerminal) {
Packit 1422b7
		/* we could not successfully parse, some unparsed items left */
Packit 1422b7
		if(left < 0) {
Packit 1422b7
			addUnparsedField(str, strLen, strLen, *json_p);
Packit 1422b7
		} else {
Packit 1422b7
			addUnparsedField(str, strLen, strLen - left, *json_p);
Packit 1422b7
		}
Packit 1422b7
	} else {
Packit 1422b7
		/* success, finalize event */
Packit 1422b7
		if(endNode->tags != NULL) {
Packit 1422b7
			/* add tags to an event */
Packit 1422b7
			json_object_get(endNode->tags);
Packit 1422b7
			json_object_object_add(*json_p, "event.tags", endNode->tags);
Packit 1422b7
			CHKR(ln_annotate(ctx, *json_p, endNode->tags));
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Gather and output pdag statistics for the full pdag (ctx)
Packit 1422b7
 * including all disconnected components (type defs).
Packit 1422b7
 *
Packit 1422b7
 * Data is sent to given file ptr.
Packit 1422b7
 */
Packit 1422b7
void
Packit 1422b7
ln_fullPTreeStats(ln_ctx ctx, FILE __attribute__((unused)) *const fp,
Packit 1422b7
const int  __attribute__((unused)) extendedStats)
Packit 1422b7
{
Packit 1422b7
	ln_displayPTree(ctx->ptree, 0);
Packit 1422b7
}