summaryrefslogtreecommitdiff
path: root/snmplib/text_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/text_utils.c')
-rw-r--r--snmplib/text_utils.c526
1 files changed, 526 insertions, 0 deletions
diff --git a/snmplib/text_utils.c b/snmplib/text_utils.c
new file mode 100644
index 0000000..32596be
--- /dev/null
+++ b/snmplib/text_utils.c
@@ -0,0 +1,526 @@
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-features.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#if HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#include <sys/types.h>
+
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#include <errno.h>
+
+#if HAVE_DMALLOC_H
+# include <dmalloc.h>
+#endif
+
+#include <net-snmp/types.h>
+#include <net-snmp/library/snmp_debug.h>
+#include <net-snmp/library/container.h>
+#include <net-snmp/library/file_utils.h>
+#include <net-snmp/library/text_utils.h>
+
+netsnmp_feature_child_of(text_utils, libnetsnmp)
+
+netsnmp_feature_provide(text_utils)
+#ifdef NETSNMP_FEATURE_REQUIRE_TEXT_UTILS
+netsnmp_feature_require(file_utils)
+#endif /* NETSNMP_FEATURE_REQUIRE_TEXT_UTILS */
+
+#ifndef NETSNMP_FEATURE_REMOVE_TEXT_UTILS
+/*------------------------------------------------------------------
+ *
+ * Prototypes
+ *
+ */
+/*
+ * parse methods
+ */
+void
+_pm_save_index_string_string(FILE *f, netsnmp_container *cin,
+ int flags);
+void
+_pm_save_everything(FILE *f, netsnmp_container *cin, int flags);
+void
+_pm_user_function(FILE *f, netsnmp_container *cin,
+ netsnmp_line_process_info *lpi, int flags);
+
+
+/*
+ * line processors
+ */
+int _process_line_tvi(netsnmp_line_info *line_info, void *mem,
+ struct netsnmp_line_process_info_s* lpi);
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Text file processing functions
+ *
+ */
+
+/**
+ * process text file, reading into extras
+ */
+netsnmp_container *
+netsnmp_file_text_parse(netsnmp_file *f, netsnmp_container *cin,
+ int parse_mode, u_int flags, void *context)
+{
+ netsnmp_container *c = cin;
+ FILE *fin;
+ int rc;
+
+ if (NULL == f)
+ return NULL;
+
+ if ((NULL == c) && (!(flags & PM_FLAG_NO_CONTAINER))) {
+ c = netsnmp_container_find("text_parse:binary_array");
+ if (NULL == c)
+ return NULL;
+ }
+
+ rc = netsnmp_file_open(f);
+ if (rc < 0) { /** error already logged */
+ if ((NULL !=c) && (c != cin))
+ CONTAINER_FREE(c);
+ return NULL;
+ }
+
+ /*
+ * get a stream from the file descriptor. This DOES NOT rewind the
+ * file (if fd was previously opened).
+ */
+ fin = fdopen(f->fd, "r");
+ if (NULL == fin) {
+ if (NS_FI_AUTOCLOSE(f->ns_flags))
+ close(f->fd);
+ if ((NULL !=c) && (c != cin))
+ CONTAINER_FREE(c);
+ return NULL;
+ }
+
+ switch (parse_mode) {
+
+ case PM_SAVE_EVERYTHING:
+ _pm_save_everything(fin, c, flags);
+ break;
+
+ case PM_INDEX_STRING_STRING:
+ _pm_save_index_string_string(fin, c, flags);
+ break;
+
+ case PM_USER_FUNCTION:
+ if (NULL != context)
+ _pm_user_function(fin, c, (netsnmp_line_process_info*)context,
+ flags);
+ break;
+
+ default:
+ snmp_log(LOG_ERR, "unknown parse mode %d\n", parse_mode);
+ break;
+ }
+
+
+ /*
+ * close the stream, which will have the side effect of also closing
+ * the file descriptor, so we need to reset it.
+ */
+ fclose(fin);
+ f->fd = -1;
+
+ return c;
+}
+
+netsnmp_feature_child_of(text_token_container_from_file, netsnmp_unused)
+#ifndef NETSNMP_FEATURE_REMOVE_TEXT_TOKEN_CONTAINER_FROM_FILE
+netsnmp_container *
+netsnmp_text_token_container_from_file(const char *file, u_int flags,
+ netsnmp_container *cin, void *context)
+{
+ netsnmp_line_process_info lpi;
+ netsnmp_container *c = cin, *c_rc;
+ netsnmp_file *fp;
+
+ if (NULL == file)
+ return NULL;
+
+ /*
+ * allocate file resources
+ */
+ fp = netsnmp_file_fill(NULL, file, O_RDONLY, 0, 0);
+ if (NULL == fp) /** msg already logged */
+ return NULL;
+
+ memset(&lpi, 0x0, sizeof(lpi));
+ lpi.mem_size = sizeof(netsnmp_token_value_index);
+ lpi.process = _process_line_tvi;
+ lpi.user_context = context;
+
+ if (NULL == c) {
+ c = netsnmp_container_find("string:binary_array");
+ if (NULL == c) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ netsnmp_file_release(fp);
+ return NULL;
+ }
+ }
+
+ c_rc = netsnmp_file_text_parse(fp, c, PM_USER_FUNCTION, 0, &lpi);
+
+ /*
+ * if we got a bad return and the user didn't pass us a container,
+ * we need to release the container we allocated.
+ */
+ if ((NULL == c_rc) && (NULL == cin)) {
+ CONTAINER_FREE(c);
+ c = NULL;
+ }
+ else
+ c = c_rc;
+
+ /*
+ * release file resources
+ */
+ netsnmp_file_release(fp);
+
+ return c;
+}
+#endif /* NETSNMP_FEATURE_REMOVE_TEXT_TOKEN_CONTAINER_FROM_FILE */
+
+
+/*------------------------------------------------------------------
+ *
+ * Text file process modes helper functions
+ *
+ */
+
+/**
+ * @internal
+ * parse mode: save everything
+ */
+void
+_pm_save_everything(FILE *f, netsnmp_container *cin, int flags)
+{
+ char line[STRINGMAX], *ptr;
+ size_t len;
+
+ netsnmp_assert(NULL != f);
+ netsnmp_assert(NULL != cin);
+
+ while (fgets(line, sizeof(line), f) != NULL) {
+
+ ptr = line;
+ len = strlen(line) - 1;
+ if (line[len] == '\n')
+ line[len] = 0;
+
+ /*
+ * save blank line or comment?
+ */
+ if (flags & PM_FLAG_SKIP_WHITESPACE) {
+ if (NULL == (ptr = skip_white(ptr)))
+ continue;
+ }
+
+ ptr = strdup(line);
+ if (NULL == ptr) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ break;
+ }
+
+ CONTAINER_INSERT(cin,ptr);
+ }
+}
+
+/**
+ * @internal
+ * parse mode:
+ */
+void
+_pm_save_index_string_string(FILE *f, netsnmp_container *cin,
+ int flags)
+{
+ char line[STRINGMAX], *ptr;
+ netsnmp_token_value_index *tvi;
+ size_t count = 0, len;
+
+ netsnmp_assert(NULL != f);
+ netsnmp_assert(NULL != cin);
+
+ while (fgets(line, sizeof(line), f) != NULL) {
+
+ ++count;
+ ptr = line;
+ len = strlen(line) - 1;
+ if (line[len] == '\n')
+ line[len] = 0;
+
+ /*
+ * save blank line or comment?
+ */
+ if (flags & PM_FLAG_SKIP_WHITESPACE) {
+ if (NULL == (ptr = skip_white(ptr)))
+ continue;
+ }
+
+ tvi = SNMP_MALLOC_TYPEDEF(netsnmp_token_value_index);
+ if (NULL == tvi) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ break;
+ }
+
+ /*
+ * copy whole line, then set second pointer to
+ * after token. One malloc, 2 strings!
+ */
+ tvi->index = count;
+ tvi->token = strdup(line);
+ if (NULL == tvi->token) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ free(tvi);
+ break;
+ }
+ tvi->value.cp = skip_not_white(tvi->token);
+ if (NULL != tvi->value.cp) {
+ *(tvi->value.cp) = 0;
+ ++(tvi->value.cp);
+ }
+ CONTAINER_INSERT(cin, tvi);
+ }
+}
+
+/**
+ * @internal
+ * parse mode:
+ */
+void
+_pm_user_function(FILE *f, netsnmp_container *cin,
+ netsnmp_line_process_info *lpi, int flags)
+{
+ char buf[STRINGMAX];
+ netsnmp_line_info li;
+ void *mem = NULL;
+ int rc;
+
+ netsnmp_assert(NULL != f);
+ netsnmp_assert(NULL != cin);
+
+ /*
+ * static buf, or does the user want the memory?
+ */
+ if (flags & PMLP_FLAG_ALLOC_LINE) {
+ if (0 != lpi->line_max)
+ li.line_max = lpi->line_max;
+ else
+ li.line_max = STRINGMAX;
+ li.line = (char *)calloc(li.line_max, 1);
+ if (NULL == li.line) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ return;
+ }
+ }
+ else {
+ li.line = buf;
+ li.line_max = sizeof(buf);
+ }
+
+ li.index = 0;
+ while (fgets(li.line, li.line_max, f) != NULL) {
+
+ ++li.index;
+ li.start = li.line;
+ li.line_len = strlen(li.line) - 1;
+ if ((!(lpi->flags & PMLP_FLAG_LEAVE_NEWLINE)) &&
+ (li.line[li.line_len] == '\n'))
+ li.line[li.line_len] = 0;
+
+ /*
+ * save blank line or comment?
+ */
+ if (!(lpi->flags & PMLP_FLAG_PROCESS_WHITESPACE)) {
+ if (NULL == (li.start = skip_white(li.start)))
+ continue;
+ }
+
+ /*
+ * do we need to allocate memory for the use?
+ * if the last call didn't use the memory we allocated,
+ * re-use it. Otherwise, allocate new chunk.
+ */
+ if ((0 != lpi->mem_size) && (NULL == mem)) {
+ mem = calloc(lpi->mem_size, 1);
+ if (NULL == mem) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ break;
+ }
+ }
+
+ /*
+ * do they want a copy ot the line?
+ */
+ if (lpi->flags & PMLP_FLAG_STRDUP_LINE) {
+ li.start = strdup(li.start);
+ if (NULL == li.start) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ break;
+ }
+ }
+ else if (lpi->flags & PMLP_FLAG_ALLOC_LINE) {
+ li.start = li.line;
+ }
+
+ /*
+ * call the user function. If the function wants to save
+ * the memory chunk, insert it in the container, the clear
+ * pointer so we reallocate next time.
+ */
+ li.start_len = strlen(li.start);
+ rc = (*lpi->process)(&li, mem, lpi);
+ if (PMLP_RC_MEMORY_USED == rc) {
+
+ if (!(lpi->flags & PMLP_FLAG_NO_CONTAINER))
+ CONTAINER_INSERT(cin, mem);
+
+ mem = NULL;
+
+ if (lpi->flags & PMLP_FLAG_ALLOC_LINE) {
+ li.line = (char *)calloc(li.line_max, 1);
+ if (NULL == li.line) {
+ snmp_log(LOG_ERR,"malloc failed\n");
+ break;
+ }
+ }
+ }
+ else if (PMLP_RC_MEMORY_UNUSED == rc ) {
+ /*
+ * they didn't use the memory. if li.start was a strdup, we have
+ * to release it. leave mem, we can re-use it (its a fixed size).
+ */
+ if (lpi->flags & PMLP_FLAG_STRDUP_LINE)
+ free(li.start); /* no point in SNMP_FREE */
+ }
+ else {
+ if (PMLP_RC_STOP_PROCESSING != rc )
+ snmp_log(LOG_ERR, "unknown rc %d from text processor\n", rc);
+ break;
+ }
+ }
+ SNMP_FREE(mem);
+}
+
+/*------------------------------------------------------------------
+ *
+ * Test line process helper functions
+ *
+ */
+/**
+ * @internal
+ * process token value index line
+ */
+int
+_process_line_tvi(netsnmp_line_info *line_info, void *mem,
+ struct netsnmp_line_process_info_s* lpi)
+{
+ netsnmp_token_value_index *tvi = (netsnmp_token_value_index *)mem;
+ char *ptr;
+
+ /*
+ * get token
+ */
+ ptr = skip_not_white(line_info->start);
+ if (NULL == ptr) {
+ DEBUGMSGTL(("text:util:tvi", "no value after token '%s'\n",
+ line_info->start));
+ return PMLP_RC_MEMORY_UNUSED;
+ }
+
+ /*
+ * null terminate, search for value;
+ */
+ *(ptr++) = 0;
+ ptr = skip_white(ptr);
+ if (NULL == ptr) {
+ DEBUGMSGTL(("text:util:tvi", "no value after token '%s'\n",
+ line_info->start));
+ return PMLP_RC_MEMORY_UNUSED;
+ }
+
+ /*
+ * get value
+ */
+ switch((int)(intptr_t)lpi->user_context) {
+
+ case PMLP_TYPE_UNSIGNED:
+ tvi->value.ul = strtoul(ptr, NULL, 0);
+ if ((errno == ERANGE) && (ULONG_MAX == tvi->value.ul))
+ snmp_log(LOG_WARNING,"value overflow\n");
+ break;
+
+
+ case PMLP_TYPE_INTEGER:
+ tvi->value.sl = strtol(ptr, NULL, 0);
+ if ((errno == ERANGE) &&
+ ((LONG_MAX == tvi->value.sl) ||
+ (LONG_MIN == tvi->value.sl)))
+ snmp_log(LOG_WARNING,"value over/under-flow\n");
+ break;
+
+ case PMLP_TYPE_STRING:
+ tvi->value.cp = strdup(ptr);
+ break;
+
+ case PMLP_TYPE_BOOLEAN:
+ if (isdigit((unsigned char)(*ptr)))
+ tvi->value.ul = strtoul(ptr, NULL, 0);
+ else if (strcasecmp(ptr,"true") == 0)
+ tvi->value.ul = 1;
+ else if (strcasecmp(ptr,"false") == 0)
+ tvi->value.ul = 0;
+ else {
+ snmp_log(LOG_WARNING,"bad value for boolean\n");
+ return PMLP_RC_MEMORY_UNUSED;
+ }
+ break;
+
+ default:
+ snmp_log(LOG_ERR,"unsupported value type %d\n",
+ (int)(intptr_t)lpi->user_context);
+ break;
+ }
+
+ /*
+ * save token and value
+ */
+ tvi->token = strdup(line_info->start);
+ tvi->index = line_info->index;
+
+ return PMLP_RC_MEMORY_USED;
+}
+
+#else /* ! NETSNMP_FEATURE_REMOVE_TEXT_UTILS */
+netsnmp_feature_unused(text_utils);
+#endif /* ! NETSNMP_FEATURE_REMOVE_TEXT_UTILS */