summaryrefslogtreecommitdiff
path: root/src/io
diff options
context:
space:
mode:
authorNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2016-05-05 14:59:28 +0200
committerNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2016-05-05 14:59:28 +0200
commit3405b0c1635843cbb81f042364bfcf238d7dc930 (patch)
tree39501fec9ec72863c929a45dbc297412bbf90688 /src/io
parentc28bb6d31a122ec983e1e0a0dd1a8bd198098c58 (diff)
Adds the current code.
It's been running for close to a month on one of the IRC channels I frequent and seems to be working fine. One should be aware that, among other missing features, this version does not store permanently what the bot learns. Indeed, I am currently using a file with 431848 lines as its initial knowledge bank, making this particular feature not a high priority one. Also consider the fact that Zero of One converts text to underscore before reading it but will not change its own aliases. This could potentially be a cause for surprises when using uppercase letters in the latter.
Diffstat (limited to 'src/io')
-rw-r--r--src/io/CMakeLists.txt8
-rw-r--r--src/io/data_input.c98
-rw-r--r--src/io/data_input.h21
-rw-r--r--src/io/data_input_types.h16
-rw-r--r--src/io/error.h146
-rw-r--r--src/io/network.c483
-rw-r--r--src/io/network.h27
-rw-r--r--src/io/network_types.h26
-rw-r--r--src/io/parameters.c354
-rw-r--r--src/io/parameters.h13
-rw-r--r--src/io/parameters_types.h20
11 files changed, 1212 insertions, 0 deletions
diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt
new file mode 100644
index 0000000..a13154b
--- /dev/null
+++ b/src/io/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(
+ SRC_FILES ${SRC_FILES}
+ ${CMAKE_CURRENT_SOURCE_DIR}/parameters.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/network.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/data_input.c
+)
+set(SRC_FILES ${SRC_FILES} PARENT_SCOPE)
+
diff --git a/src/io/data_input.c b/src/io/data_input.c
new file mode 100644
index 0000000..e31d33b
--- /dev/null
+++ b/src/io/data_input.c
@@ -0,0 +1,98 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h> /* defines SIZE_MAX */
+
+#include "error.h"
+
+#include "data_input.h"
+
+int ZoO_data_input_open
+(
+ struct ZoO_data_input di [const static 1],
+ const char filename [const restrict static 1]
+)
+{
+ /* prevents di [restrict] */
+ ZoO_strings_initialize(&(di->string));
+
+ di->file = fopen(filename, "r");
+
+ if (di->file == (FILE *) NULL)
+ {
+ ZoO_ERROR
+ (
+ "Could not open file '%s' in readonly mode.",
+ filename
+ );
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int ZoO_data_input_read_line
+(
+ struct ZoO_data_input di [const static 1],
+ ZoO_index const punctuations_count,
+ const ZoO_char punctuations [const restrict static punctuations_count]
+)
+{
+ size_t line_size, i, w_start;
+ ZoO_char * line;
+
+ /* prevents di [restrict] */
+ ZoO_strings_finalize(&(di->string));
+
+ line = (ZoO_char *) NULL;
+ line_size = 0;
+
+ /* XXX: assumed compatible with ZoO_char */
+
+ if (getline(&line, &line_size, di->file) < 1)
+ {
+ free((void *) line);
+
+ return -1;
+ }
+
+ line_size = strlen(line);
+ line[line_size - 1] = '\0';
+
+ --line_size; /* removed '\n' */
+
+ if
+ (
+ ZoO_strings_parse
+ (
+ &(di->string),
+ line_size,
+ line,
+ punctuations_count,
+ punctuations
+ ) < 0
+ )
+ {
+ free((void *) line);
+
+ return -1;
+ }
+
+ free((void *) line);
+
+ return 0;
+}
+
+void ZoO_data_input_close (struct ZoO_data_input di [const static 1])
+{
+ if (di->file != (FILE *) NULL)
+ {
+ fclose(di->file);
+
+ di->file = (FILE *) NULL;
+ }
+
+ /* prevents di [restrict] */
+ ZoO_strings_finalize(&(di->string));
+}
diff --git a/src/io/data_input.h b/src/io/data_input.h
new file mode 100644
index 0000000..a2f004b
--- /dev/null
+++ b/src/io/data_input.h
@@ -0,0 +1,21 @@
+#ifndef _ZoO_IO_DATA_INPUT_H_
+#define _ZoO_IO_DATA_INPUT_H_
+
+#include "data_input_types.h"
+
+int ZoO_data_input_open
+(
+ struct ZoO_data_input di [const static 1],
+ const char filename [const restrict static 1]
+);
+
+int ZoO_data_input_read_line
+(
+ struct ZoO_data_input di [const static 1],
+ ZoO_index const punctuations_count,
+ const ZoO_char punctuations [const restrict static punctuations_count]
+);
+
+void ZoO_data_input_close (struct ZoO_data_input di [const static 1]);
+
+#endif
diff --git a/src/io/data_input_types.h b/src/io/data_input_types.h
new file mode 100644
index 0000000..bd2709b
--- /dev/null
+++ b/src/io/data_input_types.h
@@ -0,0 +1,16 @@
+#ifndef _ZoO_IO_DATA_INPUT_TYPES_H_
+#define _ZoO_IO_DATA_INPUT_TYPES_H_
+
+#include <stdio.h>
+
+#include "../pervasive.h"
+
+#include "../tool/strings.h"
+
+struct ZoO_data_input
+{
+ FILE * restrict file;
+ struct ZoO_strings string;
+};
+
+#endif
diff --git a/src/io/error.h b/src/io/error.h
new file mode 100644
index 0000000..e4267a0
--- /dev/null
+++ b/src/io/error.h
@@ -0,0 +1,146 @@
+#ifndef _ZoO_IO_ERROR_H_
+#define _ZoO_IO_ERROR_H_
+
+#include <stdio.h>
+
+#include "../pervasive.h"
+
+#define ZoO_DEBUG_ALL 1
+
+#ifndef ZoO_DEBUG_ALL
+ #define ZoO_DEBUG_ALL 0
+#endif
+
+#ifndef ZoO_DEBUG_PROGRAM_FLOW
+ #define ZoO_DEBUG_PROGRAM_FLOW (0 || ZoO_DEBUG_ALL)
+#endif
+
+#ifndef ZoO_DEBUG_CONFIG
+ #define ZoO_DEBUG_CONFIG (0 || ZoO_DEBUG_ALL)
+#endif
+
+#ifndef ZoO_DEBUG_LEARNING
+ #define ZoO_DEBUG_LEARNING (0 || ZoO_DEBUG_ALL)
+#endif
+
+#ifndef ZoO_DEBUG_NETWORK
+ #define ZoO_DEBUG_NETWORK (0 || ZoO_DEBUG_ALL)
+#endif
+
+#define ZoO_ENABLE_WARNINGS_OUTPUT 1
+#define ZoO_ENABLE_RUNTIME_ERRORS_OUTPUT 1
+#define ZoO_ENABLE_PROGRAMMING_ERRORS_OUTPUT 1
+#define ZoO_ENABLE_FATAL_ERROR_OUTPUT 1
+
+#ifdef ZoO_ENABLE_ERROR_LOCATION
+ #define ZoO_LOCATION "[" __FILE__ "][" ZoO_TO_STRING(__LINE__) "]"
+#else
+ #define ZoO_LOCATION ""
+#endif
+
+#define ZoO_PRINT_STDERR(symbol, str, ...)\
+ fprintf(stderr, "[" symbol "]" ZoO_LOCATION " " str "\n", __VA_ARGS__);
+
+/*
+ * Given that we use preprocessor contants as flags, we can expect the compilers
+ * to remove the test condition for disabled flags. No need to be shy about
+ * allowing many debug options.
+ */
+
+#define ZoO_DEBUG(flag, str, ...)\
+ ZoO_ISOLATE\
+ (\
+ if (flag)\
+ {\
+ ZoO_PRINT_STDERR("D", str, __VA_ARGS__);\
+ }\
+ )
+
+
+#define ZoO_WARNING(str, ...)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_WARNINGS_OUTPUT)\
+ {\
+ ZoO_PRINT_STDERR("W", str, __VA_ARGS__);\
+ }\
+ )
+
+#define ZoO_ERROR(str, ...)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_RUNTIME_ERRORS_OUTPUT)\
+ {\
+ ZoO_PRINT_STDERR("E", str, __VA_ARGS__);\
+ }\
+ )
+
+#define ZoO_PROG_ERROR(str, ...)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_PROGRAMMING_ERRORS_OUTPUT)\
+ {\
+ ZoO_PRINT_STDERR("P", str, __VA_ARGS__);\
+ }\
+ )
+
+#define ZoO_FATAL(str, ...)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_FATAL_ERROR_OUTPUT)\
+ {\
+ ZoO_PRINT_STDERR("F", str, __VA_ARGS__);\
+ }\
+ )
+
+/* For outputs without dynamic content (static). ******************************/
+
+#define ZoO_PRINT_S_STDERR(symbol, str)\
+ fprintf(stderr, "[" symbol "]" ZoO_LOCATION " " str "\n");
+
+#define ZoO_S_DEBUG(flag, str)\
+ ZoO_ISOLATE\
+ (\
+ if (flag)\
+ {\
+ ZoO_PRINT_S_STDERR("D", str);\
+ }\
+ )
+
+#define ZoO_S_WARNING(str)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_WARNINGS_OUTPUT)\
+ {\
+ ZoO_PRINT_S_STDERR("W", str);\
+ }\
+ )
+
+#define ZoO_S_ERROR(str)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_RUNTIME_ERRORS_OUTPUT)\
+ {\
+ ZoO_PRINT_S_STDERR("E", str);\
+ }\
+ )
+
+#define ZoO_S_PROG_ERROR(str)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_PROGRAMMING_ERRORS_OUTPUT)\
+ {\
+ ZoO_PRINT_S_STDERR("P", str);\
+ }\
+ )
+
+#define ZoO_S_FATAL(str)\
+ ZoO_ISOLATE\
+ (\
+ if (ZoO_ENABLE_FATAL_ERROR_OUTPUT)\
+ {\
+ ZoO_PRINT_S_STDERR("F", str);\
+ }\
+ )
+
+#endif
diff --git a/src/io/network.c b/src/io/network.c
new file mode 100644
index 0000000..c8d05a2
--- /dev/null
+++ b/src/io/network.c
@@ -0,0 +1,483 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+/* "POSIX.1 does not require the inclusion of <sys/types.h>" */
+/* - man page for setsockopt */
+/* #include <sys/types.h> */
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include "error.h"
+
+#include "network.h"
+
+static int reconnect (struct ZoO_network net [const restrict static 1])
+{
+ struct timeval timeout;
+ int old_errno = errno;
+
+ errno = 0;
+ timeout.tv_sec = ZoO_NETWORK_TIMEOUT;
+ timeout.tv_usec = 0;
+
+ if (net->connection != -1)
+ {
+ close(net->connection);
+ }
+
+ net->connection =
+ socket
+ (
+ net->addrinfo->ai_family,
+ net->addrinfo->ai_socktype,
+ net->addrinfo->ai_protocol
+ );
+
+ if (net->connection == -1)
+ {
+ ZoO_FATAL
+ (
+ "Could not create socket: %s.",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ errno = 0;
+
+ if
+ (
+ (
+ setsockopt
+ (
+ net->connection,
+ SOL_SOCKET,
+ SO_RCVTIMEO,
+ (const void *) &timeout,
+ (socklen_t) sizeof(struct timeval)
+ ) < 0
+ )
+ ||
+ (
+ setsockopt
+ (
+ net->connection,
+ SOL_SOCKET,
+ SO_SNDTIMEO,
+ (const void *) &timeout,
+ (socklen_t) sizeof(struct timeval)
+ ) < 0
+ )
+ )
+ {
+ ZoO_ERROR("Could not set timeout on network socket: %s", strerror(errno));
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ errno = old_errno;
+
+ ZoO_S_DEBUG(ZoO_DEBUG_NETWORK, "(Re)connecting to network...");
+
+ if
+ (
+ connect
+ (
+ net->connection,
+ net->addrinfo->ai_addr,
+ net->addrinfo->ai_addrlen
+ ) != 0
+ )
+ {
+ ZoO_ERROR
+ (
+ "Unable to connect to the network: %s",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ errno = old_errno;
+
+ snprintf
+ (
+ net->msg,
+ 512,
+ "USER %s 8 * :%s\r\n",
+ net->user,
+ net->name
+ );
+
+ errno = 0;
+
+ if (write(net->connection, net->msg, strlen(net->msg)) < 1)
+ {
+ ZoO_ERROR
+ (
+ "Unable to write to the network: %s",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ snprintf
+ (
+ net->msg,
+ 512,
+ "NICK %s\r\n",
+ net->nick
+ );
+
+ errno = 0;
+
+ if (write(net->connection, net->msg, strlen(net->msg)) < 1)
+ {
+ ZoO_ERROR
+ (
+ "Unable to write to the network: %s",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ errno = old_errno;
+
+ net->buffer_remaining = 0;
+ net->buffer_index = 0;
+ ZoO_S_DEBUG(ZoO_DEBUG_NETWORK, "(Re)connected.");
+
+ return 0;
+}
+
+int ZoO_network_connect
+(
+ struct ZoO_network net [const static 1],
+ const char host [const restrict static 1],
+ const char port [const restrict static 1],
+ const char channel [const restrict static 1],
+ const char user [const restrict static 1],
+ const char name [const restrict static 1],
+ const char nick [const restrict static 1]
+)
+{
+ int error;
+ struct addrinfo hints;
+ const int old_errno = errno;
+
+ net->connection = -1;
+ net->channel = channel;
+ net->user = user;
+ net->name = name;
+ net->nick = nick;
+ net->buffer_index = 0;
+ net->buffer_remaining = 0;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ memset(net->msg, 0, (sizeof(ZoO_char) * 513));
+
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+
+ errno = 0;
+
+ error = getaddrinfo(host, port, &hints, &(net->addrinfo));
+
+ if (error != 0)
+ {
+ if (error == EAI_SYSTEM)
+ {
+ ZoO_ERROR
+ (
+ "Could not retrieve server information: %s.",
+ strerror(errno)
+ );
+ }
+ else
+ {
+ ZoO_FATAL
+ (
+ "Could not retrieve server information: %s.",
+ gai_strerror(error)
+ );
+ }
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ errno = 0;
+
+
+ reconnect(net);
+
+ return 0;
+}
+
+int ZoO_network_receive
+(
+ struct ZoO_network net [const restrict static 1],
+ size_t msg_offset [const restrict static 1],
+ size_t msg_size [const restrict static 1]
+)
+{
+ int old_errno;
+ ssize_t in_count, in_index, msg_index, cmd;
+
+ old_errno = errno;
+
+ for (;;)
+ {
+ msg_index = 0;
+
+ errno = 0;
+
+ while
+ (
+ (
+ (in_count =
+ read(
+ net->connection,
+ (net->buffer + net->buffer_index),
+ (512 - net->buffer_index)
+ )
+ ) > 0
+ )
+ )
+ {
+ net->buffer_remaining += in_count;
+
+ for
+ (
+ in_index = 0;
+ in_index < net->buffer_remaining;
+ ++in_index
+ )
+ {
+ net->msg[msg_index] = net->buffer[net->buffer_index + in_index];
+
+ if
+ (
+ (msg_index == 511)
+ ||
+ (
+ (msg_index > 0)
+ && (net->msg[msg_index - 1] == '\r')
+ && (net->msg[msg_index] == '\n')
+ )
+ )
+ {
+ net->msg[msg_index + 1] = '\0';
+
+
+ if (net->buffer_index != net->buffer_remaining)
+ {
+ memmove
+ (
+ net->buffer,
+ (net->buffer + net->buffer_index),
+ (size_t) net->buffer_remaining
+ );
+
+ net->buffer_index = 0;
+ }
+
+ net->buffer_remaining -= (in_index + 1);
+
+ errno = old_errno;
+
+ goto READ_MSG;
+ }
+
+ ++msg_index;
+ }
+
+ net->buffer_remaining = 0;
+ net->buffer_index = 0;
+
+ errno = 0;
+ }
+
+ ZoO_ERROR
+ (
+ "Something went wrong while trying to read from the network: %s.",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ if (reconnect(net) < 0)
+ {
+ return -1;
+ }
+
+ continue;
+
+ READ_MSG:
+
+ ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->in] %s\n", net->msg);
+
+ /* XXX: doesn't that prevent net [restrict]? */
+ if (ZoO_IS_PREFIX("PING", net->msg))
+ {
+ errno = 0;
+
+ net->msg[1] = 'O';
+
+ if (write(net->connection, net->msg, strlen(net->msg)) < 1)
+ {
+ ZoO_ERROR("Could not reply to PING request: %s", strerror(errno));
+
+ errno = old_errno;
+
+ if (reconnect(net) < 0)
+ {
+ return -1;
+ }
+
+ continue;
+ }
+
+ ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->out] %s\n", net->msg);
+
+ errno = old_errno;
+ }
+ else if (net->msg[0] == ':')
+ {
+ cmd = 0;
+
+ for (in_index = 1; in_index < 512; in_index++)
+ {
+ if (net->msg[in_index] == ' ')
+ {
+ cmd = (in_index + 1);
+
+ break;
+ }
+ }
+
+ if (cmd == 0)
+ {
+ continue;
+ }
+
+ if (ZoO_IS_PREFIX("001", (net->msg + cmd)))
+ {
+ snprintf
+ (
+ net->msg,
+ 512,
+ "JOIN :%s\r\n",
+ net->channel
+ );
+
+ errno = 0;
+
+ if (write(net->connection, net->msg, strlen(net->msg)) < 1)
+ {
+ ZoO_ERROR
+ (
+ "Could not send JOIN request: %s",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ if (reconnect(net) < 0)
+ {
+ return -1;
+ }
+ }
+
+ ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->out] %s", net->msg);
+
+ continue;
+ }
+
+ if (ZoO_IS_PREFIX("PRIVMSG", (net->msg + cmd)))
+ {
+ for (; in_index < 512; in_index++)
+ {
+ if (net->msg[in_index] == ':')
+ {
+ cmd = (in_index + 1);
+
+ break;
+ }
+ }
+
+ *msg_offset = cmd;
+ *msg_size = (msg_index - *msg_offset - 1);
+
+ /*net->msg[*msg_size - 1] = '\0'; */
+
+ return 0;
+ }
+ }
+ }
+}
+
+int ZoO_network_send (struct ZoO_network net [const restrict static 1])
+{
+ int const old_errno = errno;
+
+ snprintf
+ (
+ net->buffer,
+ 512,
+ "PRIVMSG %s :%s\r\n",
+ net->channel,
+ net->msg
+ );
+
+ errno = 0;
+
+ if (write(net->connection, net->buffer, strlen(net->buffer)) < 1)
+ {
+ ZoO_ERROR
+ (
+ "Could not send PRIVMSG: %s.",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ if (reconnect(net) < 0)
+ {
+ return -2;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ errno = old_errno;
+
+ ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->out] %s", net->buffer);
+
+ return 0;
+}
+
+void ZoO_network_disconnect (struct ZoO_network net [const restrict static 1])
+{
+ freeaddrinfo(net->addrinfo);
+ close(net->connection);
+}
+
diff --git a/src/io/network.h b/src/io/network.h
new file mode 100644
index 0000000..ac7284a
--- /dev/null
+++ b/src/io/network.h
@@ -0,0 +1,27 @@
+#ifndef _ZoO_IO_NETWORK_H_
+#define _ZoO_IO_NETWORK_H_
+#include "network_types.h"
+
+int ZoO_network_connect
+(
+ struct ZoO_network net [const static 1],
+ const char host [const restrict static 1],
+ const char port [const restrict static 1],
+ const char channel [const restrict static 1],
+ const char user [const restrict static 1],
+ const char name [const restrict static 1],
+ const char nick [const restrict static 1]
+);
+
+int ZoO_network_receive
+(
+ struct ZoO_network net [const static 1],
+ size_t msg_offset [const restrict static 1],
+ size_t msg_size [const restrict static 1]
+);
+
+int ZoO_network_send (struct ZoO_network net [const restrict static 1]);
+
+void ZoO_network_disconnect (struct ZoO_network net [const restrict static 1]);
+
+#endif
diff --git a/src/io/network_types.h b/src/io/network_types.h
new file mode 100644
index 0000000..16c81da
--- /dev/null
+++ b/src/io/network_types.h
@@ -0,0 +1,26 @@
+#ifndef _ZoO_IO_NETWORK_TYPES_H_
+#define _ZoO_IO_NETWORK_TYPES_H_
+
+#define POSIX_C_SOURCE
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "../pervasive.h"
+
+struct ZoO_network
+{
+ size_t buffer_index;
+ size_t buffer_remaining;
+ struct addrinfo * addrinfo;
+ ZoO_char buffer [513];
+ ZoO_char msg [513];
+ int connection;
+ const char * restrict channel;
+ const char * restrict user;
+ const char * restrict name;
+ const char * restrict nick;
+};
+
+#endif
diff --git a/src/io/parameters.c b/src/io/parameters.c
new file mode 100644
index 0000000..0f7d05c
--- /dev/null
+++ b/src/io/parameters.c
@@ -0,0 +1,354 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "../pervasive.h"
+
+#include "error.h"
+
+#include "parameters.h"
+
+static void load_default_parameters
+(
+ struct ZoO_parameters param [const restrict static 1]
+)
+{
+ param->data_filename = ZoO_DEFAULT_DATA_FILENAME;
+
+ param->irc_server_addr = ZoO_DEFAULT_IRC_SERVER_ADDR;
+ param->irc_server_port = ZoO_DEFAULT_IRC_SERVER_PORT;
+ param->irc_server_channel = ZoO_DEFAULT_IRC_SERVER_CHANNEL;
+ param->irc_username = ZoO_DEFAULT_IRC_USERNAME;
+ param->irc_realname = ZoO_DEFAULT_IRC_REALNAME;
+
+ param->reply_rate = ZoO_DEFAULT_REPLY_RATE;
+
+ param->aliases_count = 0;
+ param->aliases = NULL;
+}
+
+static void print_help (const char exec [const restrict static 1])
+{
+ printf
+ (
+ "Usage: %s [option_1 option_2 ...] NICKNAME [ALIAS_1 ALIAS_2 ...] \n"
+ "NICKNAME is used as the IRC nickname value.\n"
+ "If NICKNAME or any ALIAS is found in an event, the program will reply.\n"
+ "\nAvailable options:\n"
+ " [--data-filename | -df] FILENAME\n"
+ " Learn content from FILENAME before connecting.\n"
+ " Default: %s.\n"
+ " [--irc-server-addr | -isa] IRC_SERVER_ADDR\n"
+ " Connect to this server address.\n"
+ " Default: %s.\n"
+ " [--irc-server-port | -isp] IRC_SERVER_PORT\n"
+ " Connect to this server port.\n"
+ " Default: %s.\n"
+ " [--irc-server-channel | -isc] IRC_SERVER_CHANNEL\n"
+ " Connect to this server's channel.\n"
+ " Default: %s.\n"
+ " [--irc-username | -iu] USERNAME\n"
+ " Connect using this as 'username' (shown in WHOIS).\n"
+ " Default: %s.\n"
+ " [--irc-realname | -ir] REALNAME\n"
+ " Connect using this as 'realname' (shown in WHOIS).\n"
+ " Default: %s.\n"
+ " [--reply-rate | -rr] REPLY_RATE\n"
+ " Chance to reply to an event (integer, range [0, 100]).\n"
+ " Default: %d.\n",
+ exec,
+ ZoO_DEFAULT_DATA_FILENAME,
+ ZoO_DEFAULT_IRC_SERVER_ADDR,
+ ZoO_DEFAULT_IRC_SERVER_PORT,
+ ZoO_DEFAULT_IRC_SERVER_CHANNEL,
+ ZoO_DEFAULT_IRC_USERNAME,
+ ZoO_DEFAULT_IRC_REALNAME,
+ ZoO_DEFAULT_REPLY_RATE
+ );
+}
+
+static int parse_string_arg
+(
+ const char * restrict dest [const restrict static 1],
+ int const i,
+ const char * restrict argv [const restrict static 1],
+ int const argc
+)
+{
+ if (i == argc)
+ {
+ ZoO_FATAL
+ (
+ "Missing value for parameter '%s'.",
+ /* Safe: i > 1 */
+ argv[i - 1]
+ );
+
+ return -1;
+ }
+
+ *dest = argv[i];
+
+ return 0;
+}
+
+static int parse_integer_arg
+(
+ int dest [const restrict static 1],
+ int const i,
+ const char * argv [const restrict static 1],
+ int const argc,
+ int const min_val,
+ int const max_val
+)
+{
+ long int result;
+ char * endptr;
+ const int old_errno = errno;
+
+ if (i == argc)
+ {
+ ZoO_FATAL
+ (
+ "Missing value for parameter '%s'.",
+ /* Safe: i > 1 */
+ argv[i - 1]
+ );
+
+ return -1;
+ }
+
+ errno = 0;
+
+ result = strtol(argv[i], &endptr, 10);
+
+ if
+ (
+ (errno != 0)
+ || ((*endptr) == '\n')
+ || (result < min_val)
+ || (result > max_val)
+ )
+ {
+ ZoO_FATAL
+ (
+ "Invalid or missing value for parameter '%s', accepted range is "
+ "[%d, %d] (integer).",
+ /* Safe: i > 1 */
+ argv[i - 1],
+ min_val,
+ max_val
+ );
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ *dest = (int) result;
+
+ errno = old_errno;
+
+ return 0;
+}
+
+int ZoO_parameters_initialize
+(
+ struct ZoO_parameters param [const restrict static 1],
+ int const argc,
+ const char * argv [const restrict static argc]
+)
+{
+ int i;
+
+ load_default_parameters(param);
+
+ for (i = 1; i < argc; ++i)
+ {
+ if
+ (
+ (strcmp(argv[i], "--data-filename") == 0)
+ || (strcmp(argv[i], "-df") == 0)
+ )
+ {
+ i += 1;
+
+ if
+ (
+ parse_string_arg
+ (
+ &(param->data_filename),
+ i,
+ argv,
+ argc
+ ) < 0
+ )
+ {
+ return -1;
+ }
+ }
+ else if
+ (
+ (strcmp(argv[i], "--irc-server-addr") == 0)
+ || (strcmp(argv[i], "-isa") == 0)
+ )
+ {
+ i += 1;
+
+ if
+ (
+ parse_string_arg
+ (
+ &(param->irc_server_addr),
+ i,
+ argv,
+ argc
+ ) < 0
+ )
+ {
+ return -1;
+ }
+ }
+ else if
+ (
+ (strcmp(argv[i], "--irc-server-port") == 0)
+ || (strcmp(argv[i], "-isp") == 0)
+ )
+ {
+ i += 1;
+
+ if
+ (
+ parse_string_arg
+ (
+ &(param->irc_server_port),
+ i,
+ argv,
+ argc
+ ) < 0
+ )
+ {
+ return -1;
+ }
+ }
+ else if
+ (
+ (strcmp(argv[i], "--irc-server-channel") == 0)
+ || (strcmp(argv[i], "-isc") == 0)
+ )
+ {
+ i += 1;
+
+ if
+ (
+ parse_string_arg
+ (
+ &(param->irc_server_channel),
+ i,
+ argv,
+ argc
+ ) < 0
+ )
+ {
+ return -1;
+ }
+ }
+ else if
+ (
+ (strcmp(argv[i], "--irc-username") == 0)
+ || (strcmp(argv[i], "-iu") == 0)
+ )
+ {
+ i += 1;
+
+ if
+ (
+ parse_string_arg
+ (
+ &(param->irc_username),
+ i,
+ argv,
+ argc
+ ) < 0
+ )
+ {
+ return -1;
+ }
+ }
+ else if
+ (
+ (strcmp(argv[i], "--irc-realname") == 0)
+ || (strcmp(argv[i], "-in") == 0)
+ )
+ {
+ i += 1;
+
+ if
+ (
+ parse_string_arg
+ (
+ &(param->irc_realname),
+ i,
+ argv,
+ argc
+ ) < 0
+ )
+ {
+ return -1;
+ }
+ }
+ else if
+ (
+ (strcmp(argv[i], "--reply-rate") == 0)
+ || (strcmp(argv[i], "-rr") == 0)
+ )
+ {
+ i += 1;
+
+ if
+ (
+ parse_integer_arg
+ (
+ &(param->reply_rate),
+ i,
+ argv,
+ argc,
+ 0,
+ 100
+ ) < 0
+ )
+ {
+ return -1;
+ }
+ }
+ else if
+ (
+ (strcmp(argv[i], "--help") == 0)
+ || (strcmp(argv[i], "-h") == 0)
+ )
+ {
+ print_help(argv[0]);
+
+ return 0;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (i == argc)
+ {
+ ZoO_S_FATAL("Missing argument: NICKNAME");
+
+ print_help(argv[0]);
+
+ return -1;
+ }
+
+ param->aliases_count = (argc - i);
+ param->aliases = (argv + i);
+
+ return 1;
+}
diff --git a/src/io/parameters.h b/src/io/parameters.h
new file mode 100644
index 0000000..1011e2b
--- /dev/null
+++ b/src/io/parameters.h
@@ -0,0 +1,13 @@
+#ifndef _ZoO_IO_PARAMETERS_H_
+#define _ZoO_IO_PARAMETERS_H_
+
+#include "parameters_types.h"
+
+int ZoO_parameters_initialize
+(
+ struct ZoO_parameters param [const static 1],
+ int const argc,
+ const char * argv [const static argc]
+);
+
+#endif
diff --git a/src/io/parameters_types.h b/src/io/parameters_types.h
new file mode 100644
index 0000000..6d511d8
--- /dev/null
+++ b/src/io/parameters_types.h
@@ -0,0 +1,20 @@
+#ifndef _ZoO_IO_PARAMETERS_TYPES_H_
+#define _ZoO_IO_PARAMETERS_TYPES_H_
+
+struct ZoO_parameters
+{
+ const char * restrict data_filename;
+
+ const char * restrict irc_server_addr;
+ const char * restrict irc_server_port;
+ const char * restrict irc_server_channel;
+ const char * restrict irc_username;
+ const char * restrict irc_realname;
+
+ int reply_rate;
+
+ int aliases_count;
+ const char * restrict * restrict aliases;
+};
+
+#endif