Changed from the old `libconfig' to the new `liboconfig'.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 11 Feb 2007 21:12:33 +0000 (22:12 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 11 Feb 2007 21:12:33 +0000 (22:12 +0100)
Much work lies in front of us.. :/

15 files changed:
configure.in
src/Makefile.am
src/collectd.c
src/collectd.h
src/configfile.c
src/configfile.h
src/liboconfig/AUTHORS [new file with mode: 0644]
src/liboconfig/COPYING [new file with mode: 0644]
src/liboconfig/ChangeLog [new file with mode: 0644]
src/liboconfig/Makefile.am [new file with mode: 0644]
src/liboconfig/aux_types.h [new file with mode: 0644]
src/liboconfig/oconfig.c [new file with mode: 0644]
src/liboconfig/oconfig.h [new file with mode: 0644]
src/liboconfig/parser.y [new file with mode: 0644]
src/liboconfig/scanner.l [new file with mode: 0644]

index a6bc941..be43e7e 100644 (file)
@@ -24,7 +24,8 @@ AC_SUBST(LTDLINCL)
 AC_SUBST(LIBLTDL)
 AC_LIBTOOL_DLOPEN
 AC_PROG_LIBTOOL
-#AC_PROG_RANLIB
+AC_PROG_LEX
+AC_PROG_YACC
 AC_CONFIG_SUBDIRS(libltdl)
 
 #
@@ -1088,7 +1089,7 @@ AC_COLLECTD([users],     [disable], [module], [user count statistics])
 AC_COLLECTD([vserver],   [disable], [module], [vserver statistics])
 AC_COLLECTD([wireless],  [disable], [module], [wireless link statistics])
 
-AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libconfig/Makefile src/liboping/Makefile)
+AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/liboconfig/Makefile src/liboping/Makefile)
 
 cat <<EOF;
 
index aaa25f8..58cd53c 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = libconfig
+SUBDIRS = liboconfig
 if BUILD_WITH_OWN_LIBOPING
 SUBDIRS += liboping
 endif
@@ -59,8 +59,8 @@ collectd_LDFLAGS += -lstatgrab
 endif
 
 
-collectd_LDADD = $(LIBLTDL) libconfig/libconfig.la "-dlopen" self
-collectd_DEPENDENCIES = $(LIBLTDL) libconfig/libconfig.la
+collectd_LDADD = $(LIBLTDL) liboconfig/liboconfig.la "-dlopen" self
+collectd_DEPENDENCIES = $(LIBLTDL) liboconfig/liboconfig.la
 
 collectd_nagios_SOURCES = collectd-nagios.c
 
index e130647..f4dd76c 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/collectd.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -50,34 +50,49 @@ static void sigTermHandler (int signal)
        loop++;
 }
 
-static int change_basedir (char *dir)
+static int change_basedir (const char *orig_dir)
 {
-       int dirlen = strlen (dir);
+       char *dir = strdup (orig_dir);
+       int dirlen;
+       int status;
+
+       if (dir == NULL)
+       {
+               syslog (LOG_ERR, "strdup failed: %s", strerror (errno));
+               return (-1);
+       }
        
+       dirlen = strlen (dir);
        while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
                dir[--dirlen] = '\0';
 
        if (dirlen <= 0)
                return (-1);
 
-       if (chdir (dir) == -1)
+       status = chdir (dir);
+       free (dir);
+
+       if (status != 0)
        {
                if (errno == ENOENT)
                {
-                       if (mkdir (dir, 0755) == -1)
+                       if (mkdir (orig_dir, 0755) == -1)
                        {
-                               syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
+                               syslog (LOG_ERR, "mkdir (%s): %s", orig_dir,
+                                               strerror (errno));
                                return (-1);
                        }
-                       else if (chdir (dir) == -1)
+                       else if (chdir (orig_dir) == -1)
                        {
-                               syslog (LOG_ERR, "chdir (%s): %s", dir, strerror (errno));
+                               syslog (LOG_ERR, "chdir (%s): %s", orig_dir,
+                                               strerror (errno));
                                return (-1);
                        }
                }
                else
                {
-                       syslog (LOG_ERR, "chdir: %s", strerror (errno));
+                       syslog (LOG_ERR, "chdir (%s): %s", orig_dir,
+                                       strerror (errno));
                        return (-1);
                }
        }
@@ -258,12 +273,10 @@ static int start_server (void)
 #endif /* HAVE_LIBRRD */
 
 #if COLLECT_DAEMON
-static int pidfile_create (const char *file)
+static int pidfile_create (void)
 {
        FILE *fh;
-
-       if (file == NULL)
-               file = PIDFILE;
+       const char *file = global_option_get ("PIDFile");
 
        if ((fh = fopen (file, "w")) == NULL)
        {
@@ -276,14 +289,11 @@ static int pidfile_create (const char *file)
 
        return (0);
 } /* static int pidfile_create (const char *file) */
-#endif /* COLLECT_DAEMON */
 
-#if COLLECT_DAEMON
-static int pidfile_remove (const char *file)
+static int pidfile_remove (void)
 {
-       if (file == NULL) {
-               file = PIDFILE;
-       }
+       const char *file = global_option_get ("PIDFile");
+
        return (unlink (file));
 } /* static int pidfile_remove (const char *file) */
 #endif /* COLLECT_DAEMON */
@@ -292,16 +302,15 @@ int main (int argc, char **argv)
 {
        struct sigaction sigIntAction;
        struct sigaction sigTermAction;
-       char *datadir    = PKGLOCALSTATEDIR;
        char *configfile = CONFIGFILE;
+       const char *datadir;
 #if COLLECT_DAEMON
        struct sigaction sigChldAction;
-       char *pidfile    = NULL;
        pid_t pid;
        int daemonize    = 1;
 #endif
 #if COLLECT_DEBUG
-       char *logfile    = LOGFILE;
+       const char *logfile;
 #endif
 
 #if HAVE_LIBRRD
@@ -334,7 +343,7 @@ int main (int argc, char **argv)
                                break;
 #if COLLECT_DAEMON
                        case 'P':
-                               pidfile = optarg;
+                               global_option_set ("PIDFile", optarg);
                                break;
                        case 'f':
                                daemonize = 0;
@@ -347,7 +356,7 @@ int main (int argc, char **argv)
        } /* while (1) */
 
 #if COLLECT_DEBUG
-       if ((logfile = cf_get_option ("LogFile", LOGFILE)) != NULL)
+       if ((logfile = global_option_get ("LogFile")) != NULL)
                DBG_STARTFILE (logfile, "Debug file opened.");
 #endif
 
@@ -368,7 +377,7 @@ int main (int argc, char **argv)
         * Change directory. We do this _after_ reading the config and loading
         * modules to relative paths work as expected.
         */
-       if ((datadir = cf_get_option ("DataDir", PKGLOCALSTATEDIR)) == NULL)
+       if ((datadir = global_option_get ("BaseDir")) != NULL)
        {
                fprintf (stderr, "Don't have a datadir to use. This should not happen. Ever.");
                return (1);
@@ -386,13 +395,6 @@ int main (int argc, char **argv)
        sigChldAction.sa_handler = SIG_IGN;
        sigaction (SIGCHLD, &sigChldAction, NULL);
 
-       if ((pidfile == NULL)
-                       && ((pidfile = cf_get_option ("PIDFile", PIDFILE)) == NULL))
-       {
-               fprintf (stderr, "Cannot obtain pidfile. This shoud not happen. Ever.");
-               return (1);
-       }
-
        if (daemonize)
        {
                if ((pid = fork ()) == -1)
@@ -412,7 +414,7 @@ int main (int argc, char **argv)
                setsid ();
 
                /* Write pidfile */
-               if (pidfile_create (pidfile))
+               if (pidfile_create ())
                        exit (2);
 
                /* close standard descriptors */
@@ -470,7 +472,7 @@ int main (int argc, char **argv)
 
 #if COLLECT_DAEMON
        if (daemonize)
-               pidfile_remove (pidfile);
+               pidfile_remove ();
 #endif /* COLLECT_DAEMON */
 
        return (0);
index c05b3e7..7784d55 100644 (file)
 #  define COLLECTD_XFF 0.1
 #endif
 
+#define STATIC_ARRAY_LEN(array) (sizeof (array) / sizeof ((array)[0]))
+
 extern time_t curtime;
 
-#ifdef HAVE_LIBRRD
-extern int operating_mode;
-#endif
+int pidfile_set (const char *file);
+const char *pidfile_get (void);
 
 /* int main (int argc, char **argv); */
 
index 5472de4..6aafc56 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "collectd.h"
 
-#include "libconfig/libconfig.h"
+#include "liboconfig/oconfig.h"
 
 #include "common.h"
 #include "plugin.h"
 #include "network.h"
 #include "utils_debug.h"
 
-#define SHORTOPT_NONE 0
-
-#define ERR_NOT_NESTED "Sections cannot be nested.\n"
-#define ERR_SECTION_ONLY "`%s' can only be used as section.\n"
-#define ERR_NEEDS_ARG "Section `%s' needs an argument.\n"
-#define ERR_NEEDS_SECTION "`%s' can only be used within a section.\n"
-
 #define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str))
 
-#define DEBUG_CALLBACK(shortvar, var, arguments, value) \
-       DBG("shortvar = %s, var = %s, arguments = %s, value = %s, ...", \
-                       ESCAPE_NULL(shortvar), \
-                       ESCAPE_NULL(var), \
-                       ESCAPE_NULL(arguments), \
-                       ESCAPE_NULL(value))
-
-extern int operating_mode;
-
+/*
+ * Private types
+ */
 typedef struct cf_callback
 {
        const char  *type;
@@ -57,39 +44,52 @@ typedef struct cf_callback
        struct cf_callback *next;
 } cf_callback_t;
 
-static cf_callback_t *first_callback = NULL;
+typedef struct cf_value_map_s
+{
+       char *key;
+       int (*func) (const oconfig_item_t *);
+} cf_value_map_t;
 
-typedef struct cf_mode_item
+typedef struct cf_global_option_s
 {
        char *key;
        char *value;
-       int   mode;
-} cf_mode_item_t;
+       char *def;
+} cf_global_option_t;
+
+/*
+ * Prototypes of callback functions
+ */
+static int dispatch_value_pidfile (const oconfig_item_t *ci);
+static int dispatch_value_plugindir (const oconfig_item_t *ci);
+static int dispatch_value_loadplugin (const oconfig_item_t *ci);
 
-/* TODO
- * - LogFile
+/*
+ * Private variables
  */
-static cf_mode_item_t cf_mode_list[] =
+static cf_callback_t *first_callback = NULL;
+
+static cf_value_map_t cf_value_map[] =
 {
-       {"TimeToLive",  NULL, MODE_CLIENT                           },
-       {"PIDFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG },
-       {"DataDir",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG },
-       {"LogFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG }
+       {"PIDFile",    dispatch_value_pidfile},
+       {"PluginDir",  dispatch_value_plugindir},
+       {"LoadPlugin", dispatch_value_loadplugin}
 };
-static int cf_mode_num = 4;
-
-static int nesting_depth = 0;
-static char *current_module = NULL;
+static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map);
 
-/* `cf_register' needs this prototype */
-static int cf_callback_plugin_dispatch (const char *, const char *,
-               const char *, const char *, lc_flags_t, void *);
+static cf_global_option_t cf_global_options[] =
+{
+       {"BaseDir", NULL, PKGLOCALSTATEDIR},
+       {"LogFile", NULL, LOGFILE},
+       {"PIDFile", NULL, PIDFILE}
+};
+static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options);
 
 /*
  * Functions to handle register/unregister, search, and other plugin related
  * stuff
  */
-static cf_callback_t *cf_search (char *type)
+static cf_callback_t *cf_search (const char *type)
 {
        cf_callback_t *cf_cb;
 
@@ -103,7 +103,8 @@ static cf_callback_t *cf_search (char *type)
        return (cf_cb);
 }
 
-static int cf_dispatch (char *type, const char *orig_key, const char *orig_value)
+static int cf_dispatch (const char *type, const char *orig_key,
+               const char *orig_value)
 {
        cf_callback_t *cf_cb;
        char *key;
@@ -152,366 +153,229 @@ static int cf_dispatch (char *type, const char *orig_key, const char *orig_value
        return (ret);
 }
 
-void cf_unregister (const char *type)
+static int dispatch_value_pidfile (const oconfig_item_t *ci)
 {
-       cf_callback_t *this, *prev;
+       assert (strcasecmp (ci->key, "PIDFile") == 0);
+       
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
 
-       for (prev = NULL, this = first_callback;
-                       this != NULL;
-                       prev = this, this = this->next)
-               if (strcasecmp (this->type, type) == 0)
-               {
-                       if (prev == NULL)
-                               first_callback = this->next;
-                       else
-                               prev->next = this->next;
+       return (global_option_set ("PIDFile", ci->values[0].value.string));
+}
 
-                       free (this);
-                       break;
-               }
+static int dispatch_value_plugindir (const oconfig_item_t *ci)
+{
+       assert (strcasecmp (ci->key, "PluginDir") == 0);
+       
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
+
+       plugin_set_dir (ci->values[0].value.string);
+       return (0);
 }
 
-void cf_register (const char *type,
-               int (*callback) (const char *, const char *),
-               const char **keys, int keys_num)
+static int dispatch_value_loadplugin (const oconfig_item_t *ci)
 {
-       cf_callback_t *cf_cb;
-       char buf[64];
-       int i;
+       assert (strcasecmp (ci->key, "LoadPlugin") == 0);
 
-       /* Remove this module from the list, if it already exists */
-       cf_unregister (type);
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
 
-       /* This pointer will be free'd in `cf_unregister' */
-       if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
-               return;
+       return (plugin_load (ci->values[0].value.string));
+} /* int dispatch_value_loadplugin */
 
-       cf_cb->type     = type;
-       cf_cb->callback = callback;
-       cf_cb->keys     = keys;
-       cf_cb->keys_num = keys_num;
+static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
+{
+       char  buffer[4096];
+       char *buffer_ptr;
+       int   buffer_free;
+       int i;
 
-       cf_cb->next = first_callback;
-       first_callback = cf_cb;
+       buffer_ptr = buffer;
+       buffer_free = sizeof (buffer);
 
-       for (i = 0; i < keys_num; i++)
+       for (i = 0; i < ci->values_num; i++)
        {
-               if (snprintf (buf, 64, "Plugin.%s", keys[i]) < 64)
-               {
-                       /* This may be called multiple times for the same
-                        * `key', but apparently `lc_register_*' can handle
-                        * it.. */
-                       lc_register_callback (buf, SHORTOPT_NONE,
-                                       LC_VAR_STRING, cf_callback_plugin_dispatch,
-                                       NULL);
-               }
-               else
-               {
-                       DBG ("Key was truncated: `%s'", ESCAPE_NULL(keys[i]));
-               }
+               int status = -1;
+
+               if (ci->values[i].type == OCONFIG_TYPE_STRING)
+                       status = snprintf (buffer_ptr, buffer_free, " %s",
+                                       ci->values[i].value.string);
+               else if (ci->values[i].type == OCONFIG_TYPE_NUMBER)
+                       status = snprintf (buffer_ptr, buffer_free, " %lf",
+                                       ci->values[i].value.number);
+               else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
+                       status = snprintf (buffer_ptr, buffer_free, " %s",
+                                       ci->values[i].value.boolean
+                                       ? "true" : "false");
+
+               if ((status < 0) || (status >= buffer_free))
+                       return (-1);
+               buffer_free -= status;
+               buffer_ptr  += status;
        }
-}
+       /* skip the initial space */
+       buffer_ptr = buffer + 1;
 
-/*
- * Other query functions
- */
-char *cf_get_option (const char *key, char *def)
+       return (cf_dispatch (plugin, ci->key, buffer_ptr));
+} /* int plugin_conf_dispatch */
+
+static int dispatch_value (const oconfig_item_t *ci)
 {
+       int ret = -2;
        int i;
 
-       for (i = 0; i < cf_mode_num; i++)
-       {
-               if ((cf_mode_list[i].mode & operating_mode) == 0)
-                       continue;
+       for (i = 0; i < cf_value_map_num; i++)
+               if (strcasecmp (cf_value_map[i].key, ci->key) == 0)
+               {
+                       ret = cf_value_map[i].func (ci);
+                       break;
+               }
 
-               if (strcasecmp (cf_mode_list[i].key, key) != 0)
-                       continue;
+       return (ret);
+} /* int dispatch_value */
 
-               if (cf_mode_list[i].value != NULL)
-                       return (cf_mode_list[i].value);
-               return (def);
-       }
+static int dispatch_block_plugin (oconfig_item_t *ci)
+{
+       int i;
+       char *name;
 
-       return (NULL);
-}
+       if (strcasecmp (ci->key, "Plugin") != 0)
+               return (-1);
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Functions for the actual parsing                                    *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+       name = ci->values[0].value.string;
 
-/*
- * `cf_callback_mode'
- *   Chose the `operating_mode'
- *
- * Mode `value'
- */
-static int cf_callback_mode (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
-{
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if (strcasecmp (value, "Client") == 0)
-               operating_mode = MODE_CLIENT;
-#if HAVE_LIBRRD
-       else if (strcasecmp (value, "Server") == 0)
-               operating_mode = MODE_SERVER;
-       else if (strcasecmp (value, "Local") == 0)
-               operating_mode = MODE_LOCAL;
-#else /* !HAVE_LIBRRD */
-       else if (strcasecmp (value, "Server") == 0)
-       {
-               fprintf (stderr, "Invalid mode `Server': "
-                               "You need to link against librrd for this "
-                               "mode to be available.\n");
-               syslog (LOG_ERR, "Invalid mode `Server': "
-                               "You need to link against librrd for this "
-                               "mode to be available.");
-               return (LC_CBRET_ERROR);
-       }
-       else if (strcasecmp (value, "Local") == 0)
+       for (i = 0; i < ci->children_num; i++)
        {
-               fprintf (stderr, "Invalid mode `Local': "
-                               "You need to link against librrd for this "
-                               "mode to be available.\n");
-               syslog (LOG_ERR, "Invalid mode `Local': "
-                               "You need to link against librrd for this "
-                               "mode to be available.");
-               return (LC_CBRET_ERROR);
-       }
-#endif
-       else if (strcasecmp (value, "Log") == 0)
-               operating_mode = MODE_LOG;
-       else
-       {
-               syslog (LOG_ERR, "Invalid value for config option `Mode': `%s'", value);
-               return (LC_CBRET_ERROR);
+               if (ci->children[i].children == NULL)
+                       dispatch_value_plugin (name, ci->children + i);
+               else
+                       {DBG ("No nested config blocks allow for plugins. Yet.");}
        }
 
-       return (LC_CBRET_OKAY);
+       return (0);
 }
 
-/*
- * `cf_callback_mode_plugindir'
- *   Change the plugin directory
- *
- * <Mode xxx>
- *   PluginDir `value'
- * </Mode>
- */
-static int cf_callback_mode_plugindir (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
-{
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
 
-       plugin_set_dir (value);
+static int dispatch_block (oconfig_item_t *ci)
+{
+       if (strcasecmp (ci->key, "Plugin") == 0)
+               return (dispatch_block_plugin (ci));
 
-       return (LC_CBRET_OKAY);
+       return (0);
 }
 
-static int cf_callback_mode_option (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+/* 
+ * Public functions
+ */
+int global_option_set (const char *option, const char *value)
 {
-       cf_mode_item_t *item;
-
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if (extra == NULL)
-       {
-               fprintf (stderr, "No extra..?\n");
-               return (LC_CBRET_ERROR);
-       }
+       int i;
 
-       item = (cf_mode_item_t *) extra;
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, option) == 0)
+                       break;
 
-       if (strcasecmp (item->key, shortvar))
-       {
-               fprintf (stderr, "Wrong extra..\n");
-               return (LC_CBRET_ERROR);
-       }
+       if (i >= cf_global_options_num)
+               return (-1);
 
-       if ((operating_mode & item->mode) == 0)
-       {
-               fprintf (stderr, "Option `%s' is not valid in this mode!\n", shortvar);
-               return (LC_CBRET_ERROR);
-       }
+       if (cf_global_options[i].value != NULL)
+               free (cf_global_options[i].value);
 
-       if (item->value != NULL)
-       {
-               free (item->value);
-               item->value = NULL;
-       }
-
-       if ((item->value = strdup (value)) == NULL)
-       {
-               perror ("strdup");
-               return (LC_CBRET_ERROR);
-       }
+       if (value != NULL)
+               cf_global_options[i].value = strdup (value);
+       else
+               cf_global_options[i].value = NULL;
 
-       return (LC_CBRET_OKAY);
+       return (0);
 }
 
-/*
- * `cf_callback_mode_loadmodule':
- *   Load a plugin.
- *
- * <Mode xxx>
- *   LoadPlugin `value'
- * </Mode>
- */
-static int cf_callback_mode_loadmodule (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+const char *global_option_get (const char *option)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
+       int i;
 
-       if (plugin_load (value))
-               syslog (LOG_ERR, "plugin_load (%s): failed to load plugin", value);
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, option) == 0)
+                       break;
 
-       /* Return `okay' even if there was an error, because it's not a syntax
-        * problem.. */
-       return (LC_CBRET_OKAY);
-}
+       if (i >= cf_global_options_num)
+               return (NULL);
+       
+       return ((cf_global_options[i].value != NULL)
+                       ? cf_global_options[i].value
+                       : cf_global_options[i].def);
+} /* char *global_option_get */
 
-/*
- * `cf_callback_plugin'
- *   Start/end section `plugin'
- *
- * <Plugin `arguments'>
- *   ...
- * </Plugin>
- */
-static int cf_callback_plugin (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+void cf_unregister (const char *type)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if (flags == LC_FLAGS_SECTIONSTART)
-       {
-               if (nesting_depth != 0)
-               {
-                       fprintf (stderr, ERR_NOT_NESTED);
-                       return (LC_CBRET_ERROR);
-               }
-
-               if (arguments == NULL)
-               {
-                       fprintf (stderr, ERR_NEEDS_ARG, shortvar);
-                       return (LC_CBRET_ERROR);
-               }
+       cf_callback_t *this, *prev;
 
-               if ((current_module = strdup (arguments)) == NULL)
+       for (prev = NULL, this = first_callback;
+                       this != NULL;
+                       prev = this, this = this->next)
+               if (strcasecmp (this->type, type) == 0)
                {
-                       perror ("strdup");
-                       return (LC_CBRET_ERROR);
-               }
-
-               nesting_depth++;
+                       if (prev == NULL)
+                               first_callback = this->next;
+                       else
+                               prev->next = this->next;
 
-               if (cf_search (current_module) != NULL)
-                       return (LC_CBRET_OKAY);
-               else
-                       return (LC_CBRET_IGNORESECTION);
-       }
-       else if (flags == LC_FLAGS_SECTIONEND)
-       {
-               if (current_module != NULL)
-               {
-                       free (current_module);
-                       current_module = NULL;
+                       free (this);
+                       break;
                }
-
-               nesting_depth--;
-
-               return (LC_CBRET_OKAY);
-       }
-       else
-       {
-               fprintf (stderr, ERR_SECTION_ONLY, shortvar);
-               return (LC_CBRET_ERROR);
-       }
 }
 
-/*
- * `cf_callback_plugin_dispatch'
- *   Send options within `plugin' sections to the plugin that requests it.
- *
- * <Plugin `current_module'>
- *   `var' `value'
- * </Plugin>
- */
-static int cf_callback_plugin_dispatch (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+void cf_register (const char *type,
+               int (*callback) (const char *, const char *),
+               const char **keys, int keys_num)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if ((nesting_depth == 0) || (current_module == NULL))
-       {
-               fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
-               return (LC_CBRET_ERROR);
-       }
-
-       /* Send the data to the plugin */
-       if (cf_dispatch (current_module, shortvar, value) < 0)
-               return (LC_CBRET_ERROR);
-
-       return (LC_CBRET_OKAY);
-}
+       cf_callback_t *cf_cb;
 
-static void cf_init (void)
-{
-       static int run_once = 0;
-       int i;
+       /* Remove this module from the list, if it already exists */
+       cf_unregister (type);
 
-       if (run_once != 0)
+       /* This pointer will be free'd in `cf_unregister' */
+       if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
                return;
-       run_once = 1;
-
-       lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_STRING,
-                       cf_callback_mode, NULL);
-       lc_register_callback ("Plugin", SHORTOPT_NONE, LC_VAR_SECTION,
-                       cf_callback_plugin, NULL);
-
-       lc_register_callback ("PluginDir", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_mode_plugindir, NULL);
-       lc_register_callback ("LoadPlugin", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_mode_loadmodule, NULL);
 
-       for (i = 0; i < cf_mode_num; i++)
-       {
-               cf_mode_item_t *item;
-
-               item = &cf_mode_list[i];
+       cf_cb->type     = type;
+       cf_cb->callback = callback;
+       cf_cb->keys     = keys;
+       cf_cb->keys_num = keys_num;
 
-               lc_register_callback (item->key, SHORTOPT_NONE, LC_VAR_STRING,
-                               cf_callback_mode_option, (void *) item);
-       }
-}
+       cf_cb->next = first_callback;
+       first_callback = cf_cb;
+} /* void cf_register */
 
 int cf_read (char *filename)
 {
-       cf_init ();
-
-       if (filename == NULL)
-               filename = CONFIGFILE;
-
-       DBG ("Starting to parse file `%s'", filename);
+       oconfig_item_t *conf;
+       int i;
 
-       /* int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type); */
-       if (lc_process_file ("collectd", filename, LC_CONF_APACHE))
+       conf = oconfig_parse_file (filename);
+       if (conf == NULL)
        {
-               syslog (LOG_ERR, "lc_process_file (%s): %s", filename, lc_geterrstr ());
+               syslog (LOG_ERR, "Unable to read config file %s.", filename);
                return (-1);
        }
 
-       DBG ("Done parsing file `%s'", filename);
-
-       /* free memory and stuff */
-       lc_cleanup ();
+       for (i = 0; i < conf->children_num; i++)
+       {
+               if (conf->children[i].children == NULL)
+                       dispatch_value (conf->children + i);
+               else
+                       dispatch_block (conf->children + i);
+       }
 
        return (0);
-}
+} /* int cf_read */
index fec1cf4..0ee8f33 100644 (file)
@@ -63,21 +63,6 @@ void cf_register (const char *type,
 
 /*
  * DESCRIPTION
- *  `cf_get_option' returns various general options.
- *
- * PARAMETERS
- *  `key'       Name of the option to query.
- *  `def'       Pointer to return as default value.
- *
- * RETURN VALUE
- *  The pointer returned is part of an internal structure and may not be
- *  changed. If the option is not found for whatever reason (wrong key, option
- *  not allowed for currently selected mode, ...) `NULL' is returned.
- */
-char *cf_get_option (const char *key, char *def);
-
-/*
- * DESCRIPTION
  *  `cf_read' reads the config file `filename' and dispatches the read
  *  information to functions/variables. Most important: Is calls `plugin_load'
  *  to load specific plugins, depending on the current mode of operation.
@@ -93,4 +78,7 @@ char *cf_get_option (const char *key, char *def);
  */
 int cf_read (char *filename);
 
+int global_option_set (const char *option, const char *value);
+const char *global_option_get (const char *option);
+
 #endif /* defined(CONFIGFILE_H) */
diff --git a/src/liboconfig/AUTHORS b/src/liboconfig/AUTHORS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/liboconfig/COPYING b/src/liboconfig/COPYING
new file mode 100644 (file)
index 0000000..623b625
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/liboconfig/ChangeLog b/src/liboconfig/ChangeLog
new file mode 100644 (file)
index 0000000..85f0dd9
--- /dev/null
@@ -0,0 +1,2 @@
+2007-02-11, Version 0.1.0
+       * Initial release.
diff --git a/src/liboconfig/Makefile.am b/src/liboconfig/Makefile.am
new file mode 100644 (file)
index 0000000..4e8d086
--- /dev/null
@@ -0,0 +1,11 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+BUILT_SOURCES = parser.h
+CLEANFILES = parser.[ch] scanner.c
+AM_YFLAGS = -d
+
+include_HEADERS = oconfig.h
+lib_LTLIBRARIES = liboconfig.la
+
+liboconfig_la_LDFLAGS = -version-info 0:0:0 $(LEXLIB)
+liboconfig_la_SOURCES = oconfig.c oconfig.h scanner.l parser.y
diff --git a/src/liboconfig/aux_types.h b/src/liboconfig/aux_types.h
new file mode 100644 (file)
index 0000000..25b81ab
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef AUX_TYPES_H
+#define AUX_TYPES_H 1
+
+struct statement_list_s
+{
+       oconfig_item_t *statement;
+       int             statement_num;
+};
+typedef struct statement_list_s statement_list_t;
+
+struct argument_list_s
+{
+       oconfig_value_t *argument;
+       int              argument_num;
+};
+typedef struct argument_list_s argument_list_t;
+
+#endif /* AUX_TYPES_H */
diff --git a/src/liboconfig/oconfig.c b/src/liboconfig/oconfig.c
new file mode 100644 (file)
index 0000000..f8d1849
--- /dev/null
@@ -0,0 +1,84 @@
+/**
+ * oconfig - src/oconfig.c
+ * Copyright (C) 2006,2007  Florian octo Forster <octo at verplant.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "oconfig.h"
+
+/* Functions provided by the scanner */
+void yyset_in  (FILE *);
+
+oconfig_item_t *ci_root;
+
+oconfig_item_t *oconfig_parse_fh (FILE *fh)
+{
+  int status;
+  oconfig_item_t *ret;
+
+  yyset_in (fh);
+
+  status = yyparse ();
+  if (status != 0)
+  {
+    fprintf (stderr, "yyparse returned error #%i\n", status);
+    return (NULL);
+  }
+
+  ret = ci_root;
+  ci_root = NULL;
+  yyset_in ((FILE *) 0);
+
+  return (ret);
+} /* oconfig_item_t *oconfig_parse_fh */
+
+oconfig_item_t *oconfig_parse_file (const char *file)
+{
+  FILE *fh;
+  oconfig_item_t *ret;
+
+  fh = fopen (file, "r");
+  if (fh == NULL)
+  {
+    fprintf (stderr, "fopen (%s) failed: %s\n", file, strerror (errno));
+    return (NULL);
+  }
+
+  ret = oconfig_parse_fh (fh);
+  fclose (fh);
+
+  return (ret);
+} /* oconfig_item_t *oconfig_parse_file */
+
+void oconfig_free (oconfig_item_t *ci)
+{
+  int i;
+
+  if (ci->values != NULL)
+    free (ci->values);
+
+  for (i = 0; i < ci->children_num; i++)
+    oconfig_free (ci->children + i);
+}
+
+/*
+ * vim:shiftwidth=2:tabstop=8:softtabstop=2
+ */
diff --git a/src/liboconfig/oconfig.h b/src/liboconfig/oconfig.h
new file mode 100644 (file)
index 0000000..e6e53e2
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef OCONFIG_H
+#define OCONFIG_H 1
+
+#include <stdio.h>
+
+/**
+ * oconfig - src/oconfig.h
+ * Copyright (C) 2006,2007  Florian octo Forster <octo at verplant.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+/*
+ * Types
+ */
+#define OCONFIG_TYPE_STRING  0
+#define OCONFIG_TYPE_NUMBER  1
+#define OCONFIG_TYPE_BOOLEAN 2
+
+struct oconfig_value_s
+{
+  union
+  {
+    char  *string;
+    double number;
+    int    boolean;
+  } value;
+  int type;
+};
+typedef struct oconfig_value_s oconfig_value_t;
+
+struct oconfig_item_s;
+typedef struct oconfig_item_s oconfig_item_t;
+struct oconfig_item_s
+{
+  char            *key;
+  oconfig_value_t *values;
+  int              values_num;
+
+  oconfig_item_t  *parent;
+  oconfig_item_t  *children;
+  int              children_num;
+};
+
+/*
+ * Functions
+ */
+oconfig_item_t *oconfig_parse_fh (FILE *fh);
+oconfig_item_t *oconfig_parse_file (const char *file);
+
+void oconfig_free (oconfig_item_t *ci);
+
+/*
+ * vim: shiftwidth=2:tabstop=8:softtabstop=2
+ */
+#endif /* OCONFIG_H */
diff --git a/src/liboconfig/parser.y b/src/liboconfig/parser.y
new file mode 100644 (file)
index 0000000..6d1de2d
--- /dev/null
@@ -0,0 +1,220 @@
+/**
+ * oconfig - src/parser.y
+ * Copyright (C) 2007  Florian octo Forster <octo at verplant.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+%{
+#include <stdlib.h>
+#include <string.h>
+#include "oconfig.h"
+#include "aux_types.h"
+
+static char *unquote (const char *orig);
+static int yyerror (const char *s);
+
+/* Lexer variables */
+extern int yylineno;
+extern char *yytext;
+
+extern oconfig_item_t *ci_root;
+%}
+
+%start entire_file
+
+%union {
+       double  number;
+       int     boolean;
+       char   *string;
+       oconfig_value_t  cv;
+       oconfig_item_t   ci;
+       argument_list_t  al;
+       statement_list_t sl;
+}
+
+%token <number> NUMBER
+%token <boolean> TRUE FALSE
+%token <string> QUOTED_STRING UNQUOTED_STRING
+%token SLASH OPENBRAC CLOSEBRAC EOL
+
+%type <string> string
+%type <string> identifier
+/* arguments */
+%type <cv> argument
+%type <al> argument_list
+/* blocks */
+%type <ci> block_begin
+%type <ci> block
+%type <string> block_end
+/* statements */
+%type <ci> option
+%type <ci> statement
+%type <sl> statement_list
+%type <ci> entire_file
+
+%%
+string:
+       QUOTED_STRING           {$$ = unquote ($1);}
+       | UNQUOTED_STRING       {$$ = strdup ($1);}
+       ;
+
+argument:
+       NUMBER                  {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;}
+       | TRUE                  {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
+       | FALSE                 {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
+       | string                {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;}
+       ;
+
+argument_list:
+       argument_list argument
+       {
+        $$ = $1;
+        $$.argument_num++;
+        $$.argument = realloc ($$.argument, $$.argument_num * sizeof (oconfig_value_t));
+        $$.argument[$$.argument_num-1] = $2;
+       }
+       | argument
+       {
+        $$.argument = malloc (sizeof (oconfig_value_t));
+        $$.argument[0] = $1;
+        $$.argument_num = 1;
+       }
+       ;
+
+identifier:
+       UNQUOTED_STRING                 {$$ = strdup ($1);}
+       ;
+
+option:
+       identifier argument_list EOL
+       {
+        memset (&$$, '\0', sizeof ($$));
+        $$.key = $1;
+        $$.values = $2.argument;
+        $$.values_num = $2.argument_num;
+       }
+       ;
+
+block_begin:
+       OPENBRAC identifier argument_list CLOSEBRAC EOL
+       {
+        memset (&$$, '\0', sizeof ($$));
+        $$.key = $2;
+        $$.values = $3.argument;
+        $$.values_num = $3.argument_num;
+       }
+       ;
+
+block_end:
+       OPENBRAC SLASH identifier CLOSEBRAC EOL
+       {
+        $$ = $3;
+       }
+       ;
+
+block:
+       block_begin statement_list block_end
+       {
+        if (strcmp ($1.key, $3) != 0)
+        {
+               printf ("block_begin = %s; block_end = %s;\n", $1.key, $3);
+               yyerror ("Block not closed..\n");
+               exit (1);
+        }
+        $$ = $1;
+        $$.children = $2.statement;
+        $$.children_num = $2.statement_num;
+       }
+       ;
+
+statement:
+       option          {$$ = $1;}
+       | block         {$$ = $1;}
+       | EOL           {$$.values_num = 0;}
+       ;
+
+statement_list:
+       statement_list statement
+       {
+        $$ = $1;
+        if ($2.values_num > 0)
+        {
+                $$.statement_num++;
+                $$.statement = realloc ($$.statement, $$.statement_num * sizeof (oconfig_item_t));
+                $$.statement[$$.statement_num-1] = $2;
+        }
+       }
+       | statement
+       {
+        if ($1.values_num > 0)
+        {
+                $$.statement = malloc (sizeof (oconfig_item_t));
+                $$.statement[0] = $1;
+                $$.statement_num = 1;
+        }
+        else
+        {
+               $$.statement = NULL;
+               $$.statement_num = 0;
+        }
+       }
+       ;
+
+entire_file:
+       statement_list
+       {
+        ci_root = malloc (sizeof (oconfig_item_t));
+        memset (ci_root, '\0', sizeof (oconfig_item_t));
+        ci_root->children = $1.statement;
+        ci_root->children_num = $1.statement_num;
+       }
+       ;
+
+%%
+static int yyerror (const char *s)
+{
+       fprintf (stderr, "Error in line %i near `%s': %s\n", yylineno, yytext, s);
+       return (-1);
+} /* int yyerror */
+
+static char *unquote (const char *orig)
+{
+       char *ret = strdup (orig);
+       int len;
+       int i;
+
+       if (ret == NULL)
+               return (NULL);
+
+       len = strlen (ret);
+
+       if ((len < 2) || (ret[0] != '"') || (ret[len - 1] != '"'))
+               return (ret);
+
+       ret++;
+       len -= 2;
+       ret[len] = '\0';
+
+       for (i = 0; i < len; i++)
+       {
+               if (ret[i] == '\\')
+               {
+                       memmove (ret + i, ret + (i + 1), len - i);
+                       len--;
+               }
+       }
+
+       return (ret);
+} /* char *unquote */
diff --git a/src/liboconfig/scanner.l b/src/liboconfig/scanner.l
new file mode 100644 (file)
index 0000000..aa111a2
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * oconfig - src/scanner.l
+ * Copyright (C) 2007  Florian octo Forster <octo at verplant.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+%{
+#include <stdlib.h>
+#include "oconfig.h"
+#include "aux_types.h"
+#include "parser.h"
+%}
+WHITE_SPACE [\ \t\b]
+QUOTED_STRING \"([^\\"]+|\\.)*\"
+UNQUOTED_STRING [0-9A-Za-z_]+
+HEX_NUMBER 0[xX][0-9a-fA-F]+
+OCT_NUMBER 0[0-7]+
+DEC_NUMBER [\+\-]?[0-9]+
+FLOAT_NUMBER [\+\-]?[0-9]*\.[0-9]+([eE][\+\-][0-9]+)?
+NUMBER ({FLOAT_NUMBER}|{HEX_NUMBER}|{OCT_NUMBER}|{DEC_NUMBER})
+BOOL_TRUE (true|yes|on)
+BOOL_FALSE (false|no|off)
+COMMENT #.*
+PORT (6(5(5(3[0-5]|[0-2][0-9])|[0-4][0-9][0-9])|[0-4][0-9][0-9][0-9])|[1-5][0-9][0-9][0-9][0-9]|[1-9][0-9]?[0-9]?[0-9]?)
+IP_BYTE (2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])
+IPV4_ADDR {IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}(:{PORT})?
+
+%%
+{WHITE_SPACE}          |
+{COMMENT}              {/* ignore */}
+
+\n                     {return (EOL);}
+"/"                    {return (SLASH);}
+"<"                    {return (OPENBRAC);}
+">"                    {return (CLOSEBRAC);}
+{BOOL_TRUE}            {yylval.boolean = 1; return (TRUE);}
+{BOOL_FALSE}           {yylval.boolean = 0; return (FALSE);}
+
+{IPV4_ADDR}            {yylval.string = yytext; return (UNQUOTED_STRING);}
+
+{NUMBER}               {yylval.number = strtod (yytext, NULL); return (NUMBER);}
+
+{QUOTED_STRING}                {yylval.string = yytext; return (QUOTED_STRING);}
+{UNQUOTED_STRING}      {yylval.string = yytext; return (UNQUOTED_STRING);}
+%%