[syslog-ng] [PATCH 3/7] value-pairs: Introduce value_pairs_walk()

Gergely Nagy algernon at balabit.hu
Fri Sep 14 11:51:57 CEST 2012


This new function is meant to be used when one wants to process the
result of value-pairs() in a structured manner. It will treat the keys
as if they were in dotted-notation, and whenever a new object starts,
it will call obj_start_func, whenever an object ends (a new one
starts, or we're winding down in the end), it calls obj_end_func. When
processing a single key, process_value_func will be called.

The obj_start_func and obj_end_func callbacks take six arguments: the
current key name, the prefix so far, a pointer to an area where
prefix-specific data can be stored, the previous element's name and
data on the stack, and the user_data passed to value_pairs_walk(). The
process_value_func receives the value too, see the VPWalk* typedefs in
value-pairs.h.

The prefix-specific data can be NULL, so care must be taken not to
dereference it in that case.

With this functionality, we will be able to construct structured data
out of the flat representation of value-pairs.

Signed-off-by: Gergely Nagy <algernon at balabit.hu>
---
 lib/value-pairs.c |  197 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/value-pairs.h |   15 ++++
 2 files changed, 212 insertions(+)

diff --git a/lib/value-pairs.c b/lib/value-pairs.c
index 91894c8..69563c4 100644
--- a/lib/value-pairs.c
+++ b/lib/value-pairs.c
@@ -340,6 +340,203 @@ value_pairs_foreach(ValuePairs *vp, VPForeachFunc func,
                              msg, seq_num, user_data);
 }
 
+typedef struct
+{
+  GTrashStack stackp;
+  gchar *key;
+  gchar *prefix;
+  gint prefix_len;
+
+  gpointer data;
+} vp_walk_stack_t;
+
+typedef struct
+{
+  VPWalkCallbackFunc obj_start;
+  VPWalkCallbackFunc obj_end;
+  VPWalkValueCallbackFunc process_value;
+
+  gpointer user_data;
+  vp_walk_stack_t *stack;
+} vp_walk_state_t;
+
+static vp_walk_stack_t *
+vp_walker_stack_unwind_until (vp_walk_stack_t **stack, vp_walk_state_t *state,
+                              const gchar *name)
+{
+  vp_walk_stack_t *t;
+
+  if (!stack)
+    return NULL;
+  if (!*stack)
+    return NULL;
+  if (strncmp(name, (*stack)->prefix, (*stack)->prefix_len) == 0)
+    return *stack;
+
+  while ((t = g_trash_stack_pop((GTrashStack **)stack)) != NULL)
+    {
+      if (strncmp(name, t->prefix, t->prefix_len) != 0)
+        {
+          vp_walk_stack_t *p = g_trash_stack_peek((GTrashStack **)stack);
+
+          if (p)
+            state->obj_end(t->key, t->prefix, &t->data,
+                           p->prefix, &p->data,
+                           state->user_data);
+          else
+            state->obj_end(t->key, t->prefix, &t->data,
+                           NULL, NULL,
+                           state->user_data);
+          g_free(t->key);
+          g_free(t->prefix);
+          g_free(t);
+        }
+      else
+        {
+          /* This one matched, put it back, PUT IT BACK! */
+          g_trash_stack_push((GTrashStack **)stack, t);
+          break;
+        }
+    }
+
+  return *stack;
+}
+
+static void
+vp_walker_stack_unwind_all(vp_walk_stack_t **stack,
+                           vp_walk_state_t *state)
+{
+  vp_walk_stack_t *t;
+
+  while ((t = g_trash_stack_pop((GTrashStack **)stack)) != NULL)
+    {
+      vp_walk_stack_t *p = g_trash_stack_peek((GTrashStack **)stack);
+
+      if (p)
+        state->obj_end(t->key, t->prefix, &t->data,
+                       p->prefix, &p->data,
+                       state->user_data);
+      else
+        state->obj_end(t->key, t->prefix, &t->data,
+                       NULL, NULL,
+                       state->user_data);
+
+      g_free(t->key);
+      g_free(t->prefix);
+      g_free(t);
+    }
+}
+
+static vp_walk_stack_t *
+vp_walker_stack_push (vp_walk_stack_t **stack,
+                      gchar *key, gchar *prefix)
+{
+  vp_walk_stack_t *nt = g_new(vp_walk_stack_t, 1);
+
+  nt->key = key;
+  nt->prefix = prefix;
+  nt->prefix_len = strlen(nt->prefix);
+  nt->data = NULL;
+
+  g_trash_stack_push((GTrashStack **)stack, nt);
+  return nt;
+}
+
+static gchar *
+vp_walker_name_split(vp_walk_stack_t **stack, vp_walk_state_t *state,
+                     const gchar *name)
+{
+  gchar **tokens, *key = NULL;
+  guint token_cnt, i, start;
+
+  tokens = g_strsplit(name, ".", 0);
+  token_cnt = g_strv_length(tokens);
+
+  start = g_trash_stack_height((GTrashStack **)stack);
+  for (i = start; i < token_cnt - 1; i++)
+    {
+      gchar *next = tokens[i + 1];
+      vp_walk_stack_t *nt, *p = g_trash_stack_peek((GTrashStack **)stack);
+
+      tokens[i + 1] = NULL;
+      nt = vp_walker_stack_push(stack, g_strdup(tokens[i]),
+                                g_strjoinv(".", tokens));
+      tokens[i + 1] = next;
+
+      if (p)
+        state->obj_start(nt->key, nt->prefix, &nt->data,
+                         p->prefix, &p->data,
+                         state->user_data);
+      else
+        state->obj_start(nt->key, nt->prefix, &nt->data,
+                         NULL, NULL, state->user_data);
+    }
+
+  /* The last token is the key (well, second to last, last being
+     NULL), so treat that normally. */
+  key = g_strdup(tokens[token_cnt - 1]);
+  g_strfreev(tokens);
+
+  return key;
+}
+
+static gboolean
+value_pairs_walker(const gchar *name, const gchar *value,
+                   gpointer user_data)
+{
+  vp_walk_state_t *state = (vp_walk_state_t *)user_data;
+  vp_walk_stack_t *st = g_trash_stack_peek((GTrashStack **)&state->stack);
+  gchar *key;
+  gboolean result;
+
+  st = vp_walker_stack_unwind_until (&st, state, name);
+
+  key = vp_walker_name_split (&st, state, name);
+
+  if (st)
+    result = state->process_value(key, st->prefix, value, &st->data,
+                                  state->user_data);
+  else
+    result = state->process_value(key, NULL, value, NULL,
+                                  state->user_data);
+
+  g_free(key);
+
+  /* And the new stack becomes whatever we have in st now. */
+  state->stack = st;
+
+  return result;
+}
+
+static gint
+vp_walk_cmp(const gchar *s1, const gchar *s2)
+{
+  return strcmp(s2, s1);
+}
+
+void
+value_pairs_walk(ValuePairs *vp,
+                 VPWalkCallbackFunc obj_start_func,
+                 VPWalkValueCallbackFunc process_value_func,
+                 VPWalkCallbackFunc obj_end_func,
+                 LogMessage *msg, gint32 seq_num,
+                 gpointer user_data)
+{
+  vp_walk_state_t state;
+
+  state.user_data = user_data;
+  state.obj_start = obj_start_func;
+  state.obj_end = obj_end_func;
+  state.process_value = process_value_func;
+  state.stack = NULL;
+
+  state.obj_start(NULL, NULL, NULL, NULL, NULL, user_data);
+  value_pairs_foreach_sorted(vp, value_pairs_walker,
+                             (GCompareDataFunc)vp_walk_cmp, msg, seq_num, &state);
+  vp_walker_stack_unwind_all(&state.stack, &state);
+  state.obj_end(NULL, NULL, NULL, NULL, NULL, user_data);
+}
+
 static void
 value_pairs_init_set(ValuePairSpec *set)
 {
diff --git a/lib/value-pairs.h b/lib/value-pairs.h
index 1de97ca..ae84fd2 100644
--- a/lib/value-pairs.h
+++ b/lib/value-pairs.h
@@ -31,6 +31,14 @@
 typedef struct _ValuePairs ValuePairs;
 typedef gboolean (*VPForeachFunc)(const gchar *name, const gchar *value, gpointer user_data);
 
+typedef gboolean (*VPWalkValueCallbackFunc)(const gchar *name, const gchar *prefix,
+                                            const gchar *value,
+                                            gpointer *prefix_data, gpointer user_data);
+typedef gboolean (*VPWalkCallbackFunc)(const gchar *name,
+                                       const gchar *prefix, gpointer *prefix_data,
+                                       const gchar *prev, gpointer *prev_data,
+                                       gpointer user_data);
+
 gboolean value_pairs_add_scope(ValuePairs *vp, const gchar *scope);
 void value_pairs_add_glob_pattern(ValuePairs *vp, const gchar *pattern, gboolean include);
 void value_pairs_add_pair(ValuePairs *vp, GlobalConfig *cfg, const gchar *key, const gchar *value);
@@ -45,6 +53,13 @@ void value_pairs_foreach(ValuePairs *vp, VPForeachFunc func,
                          LogMessage *msg, gint32 seq_num,
                          gpointer user_data);
 
+void value_pairs_walk(ValuePairs *vp,
+                      VPWalkCallbackFunc obj_start_func,
+                      VPWalkValueCallbackFunc process_value_func,
+                      VPWalkCallbackFunc obj_end_func,
+                      LogMessage *msg, gint32 seq_num,
+                      gpointer user_data);
+
 ValuePairs *value_pairs_new(void);
 void value_pairs_free(ValuePairs *vp);
 
-- 
1.7.10.4




More information about the syslog-ng mailing list