start reworking option-parsing, but --module/--config is still unimplemented
authorTero Marttila <terom@fixme.fi>
Mon, 16 Mar 2009 00:55:07 +0200
changeset 63 d399a1d915a3
parent 62 e0dabc496f64
child 64 81cc5b8c5abc
start reworking option-parsing, but --module/--config is still unimplemented
src/error.c
src/error.h
src/irc_client.c
src/irc_client.h
src/irc_net.c
src/irc_net.h
src/nexus.c
--- a/src/error.c	Mon Mar 16 00:11:15 2009 +0200
+++ b/src/error.c	Mon Mar 16 00:55:07 2009 +0200
@@ -17,6 +17,7 @@
     {   ERR_EVENT_NEW,                      "event_new",                                ERR_EXTRA_NONE      },
     {   ERR_EVENT_ADD,                      "event_add",                                ERR_EXTRA_NONE      },
     {   ERR_EVSQL_NEW_PQ,                   "evsql_new_pq",                             ERR_EXTRA_NONE      },
+    {   ERR_CMD_OPT,                        "argv",                                     ERR_EXTRA_STR       },
     {   _ERR_INVALID,                       NULL,                                       0                   }
 
 }, _sock_error_desc[] = {
--- a/src/error.h	Mon Mar 16 00:11:15 2009 +0200
+++ b/src/error.h	Mon Mar 16 00:55:07 2009 +0200
@@ -83,6 +83,10 @@
 
     /** @see module_error_code */
     _ERR_MODULE     = 0x000a00,
+
+    /** General errors */
+    _ERR_GENERAL    = 0xffff00,
+    ERR_CMD_OPT,
 };
 
 /**
--- a/src/irc_client.c	Mon Mar 16 00:11:15 2009 +0200
+++ b/src/irc_client.c	Mon Mar 16 00:55:07 2009 +0200
@@ -37,22 +37,22 @@
     free(client);
 }
 
-err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, struct irc_net_info *net_info)
+err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, struct irc_net_info *net_info, struct error_info *err)
 {
     struct irc_net *net;
-    struct error_info err;
 
     // create the new irc_chan struct
-    if (irc_net_create(&net, net_info, &err))
-        return ERROR_CODE(&err);
+    if (irc_net_create(&net, net_info, err))
+        return ERROR_CODE(err);
     
     // add to network list
     TAILQ_INSERT_TAIL(&client->networks, net, client_networks);
     
     // ok
-    *net_ptr = net;
+    if (net_ptr)
+        *net_ptr = net;
 
-    return SUCCESS;
+    return SET_ERROR(err, SUCCESS);
 }
 
 struct irc_net* irc_client_get_net (struct irc_client *client, const char *network)
--- a/src/irc_client.h	Mon Mar 16 00:11:15 2009 +0200
+++ b/src/irc_client.h	Mon Mar 16 00:55:07 2009 +0200
@@ -35,8 +35,12 @@
 
 /**
  * Add a new IRC network
+ *
+ * @param client the irc_client state
+ * @param net_ptr used to return the new irc_net if not NULL
+ * @param net_info info required to identify and connect to the network
  */
-err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, struct irc_net_info *net_info);
+err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, struct irc_net_info *net_info, struct error_info *err);
 
 /**
  * Get a pre-existing IRC network by name
--- a/src/irc_net.c	Mon Mar 16 00:11:15 2009 +0200
+++ b/src/irc_net.c	Mon Mar 16 00:55:07 2009 +0200
@@ -220,15 +220,13 @@
     free(net);
 }
 
-struct irc_chan* irc_net_add_chan (struct irc_net *net, const struct irc_chan_info *info)
+err_t irc_net_add_chan (struct irc_net *net, struct irc_chan **chan_ptr, const struct irc_chan_info *info, struct error_info *err)
 {
     struct irc_chan *chan;
-    struct error_info err;
 
     // create the new irc_chan struct
-    if (irc_chan_create(&chan, net, info, &err))
-        // XXX: we lose error info
-        return NULL;
+    if (irc_chan_create(&chan, net, info, err))
+        return ERROR_CODE(err);
     
     // add to network list
     TAILQ_INSERT_TAIL(&net->channels, chan, node);
@@ -236,13 +234,15 @@
     // currently connected?
     if (net->conn && net->conn->registered) {
         // then join
-        if ((ERROR_CODE(&err) = irc_chan_join(chan)))
-            // XXX
-            return NULL;
+        if ((ERROR_CODE(err) = irc_chan_join(chan)))
+            return ERROR_CODE(err);
     }
 
-    // ok, return
-    return chan;
+    // ok
+    if (chan_ptr)
+        *chan_ptr = chan;
+
+    return SUCCESS;
 }
 
 struct irc_chan* irc_net_get_chan (struct irc_net *net, const char *channel)
--- a/src/irc_net.h	Mon Mar 16 00:11:15 2009 +0200
+++ b/src/irc_net.h	Mon Mar 16 00:55:07 2009 +0200
@@ -74,7 +74,7 @@
  *
  * If we are connected and registered, JOIN the channel right away, otherwise, join it once we register.
  */
-struct irc_chan* irc_net_add_chan (struct irc_net *net, const struct irc_chan_info *info);
+err_t irc_net_add_chan (struct irc_net *net, struct irc_chan **chan_ptr, const struct irc_chan_info *info, struct error_info *err);
 
 /**
  * Look up an existing irc_chan by name, returning NULL if not found.
--- a/src/nexus.c	Mon Mar 16 00:11:15 2009 +0200
+++ b/src/nexus.c	Mon Mar 16 00:55:07 2009 +0200
@@ -1,5 +1,4 @@
 #include "nexus.h"
-#include "irc_log.h"
 #include "signals.h"
 #include "log.h"
 
@@ -8,41 +7,177 @@
 #include <stdio.h>
 #include <getopt.h>
 #include <signal.h>
+#include <string.h>
 
 #define DEFAULT_HOST "irc.fixme.fi"
 #define DEFAULT_PORT "6667"
 #define DEFAULT_PORT_SSL "6697"
 
+/**
+ * Command-line option codes
+ */
 enum option_code {
-    _OPT_LOG_BEGIN      = 0x00ff,
+    OPT_HELP            = 'h',
+    OPT_NETWORK         = 'n',
+    OPT_CHANNEL         = 'c',
+    OPT_MODULE          = 'm',
+    OPT_CONFIG          = 'C',
+    
+    /** Options without short names */
+    _OPT_EXT_BEGIN      = 0x00ff,
 
-    OPT_LOG_DATABASE,
-    OPT_LOG_CHANNEL,
 };
 
+/**
+ * Command-line option definitions
+ */
 static struct option options[] = {
-    {"help",            0,  NULL,   'h'                 },
-    {"hostname",        1,  NULL,   'H'                 },
-    {"port",            1,  NULL,   'P'                 },
-    {"ssl",             0,  NULL,   'S'                 },
-    {"log-database",    1,  NULL,   OPT_LOG_DATABASE    },
-    {"log-channel",     1,  NULL,   OPT_LOG_CHANNEL     },
-    {0,                 0,  0,      0                   },
+    {"help",            0,  NULL,   OPT_HELP        },
+    {"network",         1,  NULL,   OPT_NETWORK     },
+    {"channel",         1,  NULL,   OPT_CHANNEL     },
+    {"module",          1,  NULL,   OPT_MODULE      },
+    {"config",          1,  NULL,   OPT_CONFIG      },
+    {0,                 0,  0,      0               },
 };
 
-void usage (const char *exe) 
+/**
+ * Display --help output on stdout
+ */
+static void usage (const char *exe) 
 {
     printf("Usage: %s [OPTIONS]\n", exe);
     printf("\n");
     printf(" --help / -h            display this message\n");
-    printf(" --hostname / -H HOST   set hostname to connect to\n");
-    printf(" --port / -P PORT       set service port to connect to\n");
-    printf(" --ssl / -S             use SSL\n");
-    printf(" --log-database         database connection string for logging\n");
-    printf(" --log-channel          channel to log\n");
+    printf(" --network / -n         add an IRC network using '<name>:<hostname>[:<port>[:ssl]]' format\n");
+    printf(" --channel / -c         add an IRC channel using '<network>:<channel>' format\n");
+    printf(" --module / -m          add a module using '<name>:<path>' format\n");
+    printf(" --config / -C          add a module configuration option using '<mod_name>:<name>[:<value>]' format\n");
 }
 
-void on_sigint (evutil_socket_t sig, short what, void *arg)
+/**
+ * Parse and apply a --network option
+ */
+static err_t apply_network (struct nexus *nexus, char *opt, struct error_info *err)
+{
+    struct irc_net_info info = {
+        .network            = NULL,
+        .hostname           = NULL,
+        .service            = DEFAULT_PORT,
+        .use_ssl            = false,
+        .register_info      = {
+            .nickname       = "SpBotDev",
+            .username       = "spbot-dev",
+            .realname       = "SpBot (development version)"
+        }
+    };
+
+    // parse the required fields
+    if ((info.network = strsep(&opt, ":")) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <name> field for --network");
+    
+    if ((info.hostname = strsep(&opt, ":")) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <hostname> field for --network");
+    
+    // parse the optional fields
+    if (opt)
+        info.service = strsep(&opt, ":");
+    
+    // parse any remaining flags
+    while (opt) {
+        char *flag = strsep(&opt, ":");
+
+        if (strcmp(flag, "ssl"))
+            info.use_ssl = true;
+
+        else
+            RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unrecognized flag for --network");
+    }
+
+    // create the net
+    if (irc_client_add_net(nexus->client, NULL, &info, err))
+        return ERROR_CODE(err);
+
+    // ok
+    return SET_ERROR(err, SUCCESS);
+}
+
+/**
+ * Parse and apply a --channel option
+ */
+static err_t apply_channel (struct nexus *nexus, char *opt, struct error_info *err)
+{
+    const char *network = NULL;
+    struct irc_net *net;
+    struct irc_chan_info info = {
+        .channel            = NULL,
+    };
+
+    // parse the required fields
+    if ((network = strsep(&opt, ":")) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <network> field for --channel");
+    
+    if ((info.channel = strsep(&opt, ":")) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <channel> field for --channel");
+
+    // trailing garbage?
+    if (opt)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel");
+
+    // look up the net
+    if ((net = irc_client_get_net(nexus->client, network)) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unknown network for --channel");
+
+    // add the channel
+    if (irc_net_add_chan(net, NULL, &info, err))
+        return ERROR_CODE(err);
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Parse arguments and apply them to the given nexus
+ */
+static err_t parse_args (struct nexus *nexus, int argc, char **argv, struct error_info *err)
+{
+    int opt, option_index;
+    
+    // parse options
+    while ((opt = getopt_long(argc, argv, "hn:c:m:C:", options, &option_index)) != -1) {
+        switch (opt) {
+            case OPT_HELP:
+                usage(argv[0]);
+
+                // XXX: return instead
+                exit(EXIT_SUCCESS);
+            
+            case OPT_NETWORK:
+                if (apply_network(nexus, optarg, err))
+                    return ERROR_CODE(err);
+
+                break;
+
+            case OPT_CHANNEL:
+                if (apply_channel(nexus, optarg, err))
+                    return ERROR_CODE(err);
+
+                break;
+            
+            case OPT_MODULE:
+            case OPT_CONFIG:
+                RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "option unimplemented");
+
+            case '?':
+                usage(argv[0]);
+                return SET_ERROR(err, ERR_CMD_OPT);
+        }
+    }
+    
+    // ok
+    return SUCCESS;
+}
+
+static void on_sigint (evutil_socket_t sig, short what, void *arg)
 {
     struct nexus *ctx = arg;
 
@@ -71,135 +206,47 @@
 
 int main (int argc, char **argv) 
 {
-    int opt, option_index;
     struct signals *signals;
-    struct nexus ctx;
-    struct irc_net *net;
     struct error_info err;
 
-    struct irc_net_info net_info = {
-        .network                    = "default",
-        .hostname                   = DEFAULT_HOST,
-        .service                    = DEFAULT_PORT,
-        .use_ssl                    = false,
-        .register_info              = {
-            .nickname               = "SpBotDev",
-            .username               = "spbot-dev",
-            .realname               = "SpBot (development version)",
-        }
-    };
-
-    // XXX: hardcode irc_log config
-    char *log_db_info = NULL;
-    struct irc_chan_info log_chan_info = {
-        .channel                    = NULL, 
-    };
-
-    bool port_default = true;
-    
-    // parse options
-    while ((opt = getopt_long(argc, argv, "hH:P:S", options, &option_index)) != -1) {
-        switch (opt) {
-            case 'h':
-                usage(argv[0]);
-                return EXIT_SUCCESS;
+    struct nexus _nexus, *nexus = &_nexus;
 
-            case 'H':
-                net_info.hostname = optarg;
-                break;
-            
-            case 'P':
-                net_info.service = optarg;
-                port_default = false;
-                break;
-
-            case 'S':
-                net_info.use_ssl = true;
-
-                if (port_default)
-                    net_info.service = DEFAULT_PORT_SSL;
-
-                break;
-            
-            case OPT_LOG_DATABASE:
-                log_db_info = optarg;
-                break;
-
-            case OPT_LOG_CHANNEL:
-                log_chan_info.channel = optarg;
-                break;
-
-            case '?':
-                usage(argv[0]);
-                return EXIT_FAILURE;
-        }
-    }
+    // zero nexus
+    memset(nexus, 0, sizeof(*nexus));
 
     // initialize libevent
-    if ((ctx.ev_base = event_base_new()) == NULL)
+    if ((nexus->ev_base = event_base_new()) == NULL)
         FATAL("event_base_new");
     
     // initialize signal handlers
-    if ((ERROR_CODE(&err) = signals_create(&signals, ctx.ev_base)))
+    if ((ERROR_CODE(&err) = signals_create(&signals, nexus->ev_base)))
         FATAL("signals_create");
 
-    // initialize sock module
-    if (sock_init(ctx.ev_base, &err))
-        FATAL_ERROR(&err, "sock_init");
-
-    // modules 
-    if ((ERROR_CODE(&err) = modules_create(&ctx.modules, &ctx)))
-        FATAL_ERROR(&err, "modules_create");
-    
-    // the IRC client
-    if (irc_client_create(&ctx.client, &err))
-        FATAL_ERROR(&err, "irc_client_create");
-
-    // the IRC network
-    if (irc_client_add_net(ctx.client, &net, &net_info))
-        FATAL_ERR(ERROR_CODE(&err), "irc_client_add_net");
-
     // add our signal handlers
     if (
             (ERROR_CODE(&err) = signals_add(signals, SIGPIPE, &signals_ignore, signals))
-        ||  (ERROR_CODE(&err) = signals_add(signals, SIGINT, &on_sigint, &ctx))
+        ||  (ERROR_CODE(&err) = signals_add(signals, SIGINT, &on_sigint, nexus))
     )
         FATAL_ERROR(&err, "signals_add");
-    
-    // logging?
-    if (log_db_info || log_chan_info.channel) {
-        struct module *mod_irc_log;
-
-        struct module_info mod_irc_log_info = {
-            .name = "irc_log",
-            .path = "modules/libirc_log.so"
-        };
-
-        // load the module
-        if (module_load(ctx.modules, &mod_irc_log, &mod_irc_log_info, &err))
-            FATAL_ERROR(&err, "module_load");
+ 
+    // initialize sock module
+    if (sock_init(nexus->ev_base, &err))
+        FATAL_ERROR(&err, "sock_init");
 
-        // get the channel
-        if (log_chan_info.channel) {
-            char conf_channel[] = "default/#test";
-
-            // create the channel
-            if ((irc_net_add_chan(net, &log_chan_info)) == NULL)
-                FATAL("irc_net_add_chan");
+    // modules 
+    if ((ERROR_CODE(&err) = modules_create(&nexus->modules, nexus)))
+        FATAL_ERROR(&err, "modules_create");
+    
+    // the IRC client
+    if (irc_client_create(&nexus->client, &err))
+        FATAL_ERROR(&err, "irc_client_create");
 
-            // configure it
-            // XXX: hardcoded
-            if (module_conf(mod_irc_log, "channel", conf_channel, &err))
-                FATAL_ERROR(&err, "module_conf(irc_log, '%s', '%s)", "channel", conf_channel);
-        }
-
-        // configure the databse info
-        if (log_db_info && module_conf(mod_irc_log, "db_info", log_db_info, &err))
-            FATAL_ERROR(&err, "module_conf(irc_log, 'db_info', '%s')", log_db_info);
-    }
-
+    // parse args
+    if (parse_args(nexus, argc, argv, &err))
+        FATAL_ERROR(&err, "parse_args");
+   
     // run event loop
-    if (event_base_dispatch(ctx.ev_base))
+    if (event_base_dispatch(nexus->ev_base))
         FATAL("event_base_dispatch");
     
     // ok, no cleanup