[syslog-ng] [PATCH] basicfuncs: Implement a $(substr STR START [LEN]) template function.

Gergely Nagy algernon at balabit.hu
Sun May 1 10:12:04 CEST 2011


This is an implementation of a $(substr <string> <start> [<len>])
template function, where the first argument is the string we want to
extract a part from, the second is the starting offset, and the third,
optional is the length of the substring we want to extract.

If no length is specified, then the substring will be extracted from
the desired offset until the end of the original string. Offset 0
means the start of the string.

Negative offsets/lengths works in a manner similar to other
implementations:

- negative offset means "start that many characters counting from the
  end of the string", with -1 being the last character
- negative length means "return substring from desired start up to
  that many characters from the end of the string"

Signed-off-by: Jakub Jankowski <shasta at toxcorp.com>
Signed-off-by: Gergely Nagy <algernon at balabit.hu>
---
 modules/basicfuncs/basic-funcs.c |  124 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 124 insertions(+), 0 deletions(-)

diff --git a/modules/basicfuncs/basic-funcs.c b/modules/basicfuncs/basic-funcs.c
index 44b8af3..a03e221 100644
--- a/modules/basicfuncs/basic-funcs.c
+++ b/modules/basicfuncs/basic-funcs.c
@@ -4,6 +4,9 @@
 #include "filter-expr-parser.h"
 #include "cfg.h"
 
+#include <stdlib.h>
+#include <errno.h>
+
 static void
 tf_echo(LogMessage *msg, gint argc, GString *argv[], GString *result)
 {
@@ -19,6 +22,126 @@ tf_echo(LogMessage *msg, gint argc, GString *argv[], GString *result)
 
 TEMPLATE_FUNCTION_SIMPLE(tf_echo);
 
+static gboolean
+tf_substr_parse_int(const gchar *s, long *d)
+{
+  gchar *endptr;
+  glong val;
+
+  errno = 0;
+  val = strtoll(s, &endptr, 10);
+
+  if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+      || (errno != 0 && val == 0))
+    return FALSE;
+
+  if (endptr == s || *endptr != '\0')
+    return FALSE;
+
+  *d = val;
+  return TRUE;
+}
+
+static void
+tf_substr(LogMessage *msg, gint argc, GString *argv[], GString *result)
+{
+  glong start, len;
+
+  /*
+   * We need to cast argv[0]->len, which is gsize (so unsigned) type, to a
+   * signed type, so we can compare negative offsets/lengths with string
+   * length. But if argv[0]->len is bigger than G_MAXLONG, this can lead to
+   * completely wrong calculations, so we'll just return nothing (alternative
+   * would be to return original string and perhaps print an error...)
+   */
+  if (argv[0]->len >= G_MAXLONG) {
+    msg_error("$(substr) error: string is too long", NULL);
+    return;
+  }
+
+  /* check number of arguments */
+  if (argc < 2 || argc > 3)
+    return;
+
+  /* get offset position from second argument */
+  if (!tf_substr_parse_int(argv[1]->str, &start)) {
+    msg_error("$(substr) parsing failed, start could not be parsed", evt_tag_str("start", argv[1]->str), NULL);
+    return;
+  }
+
+  /* if we were called with >2 arguments, third was desired length */
+  if (argc > 2) {
+    if (!tf_substr_parse_int(argv[2]->str, &len)) {
+      msg_error("$(substr) parsing failed, length could not be parsed", evt_tag_str("length", argv[2]->str), NULL);
+      return;
+    }
+  } else
+    len = (glong)argv[0]->len;
+
+  /*
+   * if requested substring length is negative and beyond string start, do nothing;
+   * also if it is greater than string length, limit it to string length
+   */
+  if (len < 0 && -(len) > (glong)argv[0]->len)
+    return;
+  else if (len > (glong)argv[0]->len)
+    len = (glong)argv[0]->len;
+
+  /*
+   * if requested offset is beyond string length, do nothing;
+   * also, if offset is negative and "before" string beginning, do nothing
+   * (or should we perhaps align start with the beginning instead?)
+   */
+  if (start >= (glong)argv[0]->len)
+    return;
+  else if (start < 0 && -(start) > (glong)argv[0]->len)
+    return;
+
+  /* with negative length, see if we don't end up with start > end */
+  if (len < 0 && ((start < 0 && start > len) ||
+		  (start >= 0 && (len + ((glong)argv[0]->len) - start) < 0)))
+    return;
+
+  /* if requested offset is negative, move start it accordingly */
+  if (start < 0) {
+    start = start + (glong)argv[0]->len;
+    /*
+     * this shouldn't actually happen, as earlier we tested for
+     * (start < 0 && -start > argv0len), but better safe than sorry
+     */
+    if (start < 0)
+      start = 0;
+  }
+
+  /*
+   * if requested length is negative, "resize" len to include exactly as many
+   * characters as needed from the end of the string, given our start position.
+   * (start is always non-negative here already)
+   */
+  if (len < 0) {
+    len = ((glong)argv[0]->len) - start + len;
+    /* this also shouldn't happen, but - again - better safe than sorry */
+    if (len < 0)
+      return;
+  }
+
+  /* if we're beyond string end, do nothing */
+  if (start >= (glong)argv[0]->len)
+    return;
+
+  /* if we want something beyond string end, do it only till the end */
+  if (start + len > (glong)argv[0]->len)
+    len = ((glong)argv[0]->len) - start;
+
+  /* skip g_string_append_len if we ended up having to extract 0 chars */
+  if (len == 0)
+    return;
+
+  g_string_append_len(result, argv[0]->str + start, len);
+}
+
+TEMPLATE_FUNCTION_SIMPLE(tf_substr);
+
 typedef struct _TFCondState
 {
   FilterExprNode *filter;
@@ -158,6 +281,7 @@ static Plugin basicfuncs_plugins[] =
   TEMPLATE_FUNCTION_PLUGIN(tf_echo, "echo"),
   TEMPLATE_FUNCTION_PLUGIN(tf_grep, "grep"),
   TEMPLATE_FUNCTION_PLUGIN(tf_if, "if"),
+  TEMPLATE_FUNCTION_PLUGIN(tf_substr, "substr"),
 };
 
 gboolean
-- 
1.7.2.5




More information about the syslog-ng mailing list