aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2017-04-22 17:55:59 +0200
committerNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2017-04-22 17:55:59 +0200
commit1e96b266a195338cb1721aa76313cce6a1b96950 (patch)
tree8c1056871a0a49af3723bf9efa361000064097c4
downloadcli-1e96b266a195338cb1721aa76313cce6a1b96950.zip
cli-1e96b266a195338cb1721aa76313cce6a1b96950.tar.bz2
Initial Commit.
-rw-r--r--CMakeLists.txt24
-rw-r--r--LICENSE27
-rw-r--r--README.md23
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--src/error/CMakeLists.txt6
-rw-r--r--src/error/error.h143
-rw-r--r--src/main.c209
-rw-r--r--src/pervasive.h28
8 files changed, 468 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..662815f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
+
+project("JabberHive - Command Line Interface")
+
+include(FindPkgConfig)
+
+add_subdirectory(src)
+add_definitions(-D_POSIX_SOURCE)
+add_definitions(-D_POSIX_C_SOURCE=200809L)
+
+if(CMAKE_COMPILER_IS_GNUCC)
+ message(STATUS "GNUCC detected. Adding '-O3' parameter.")
+ set(CMAKE_C_FLAGS "-O3")
+ #set(CMAKE_C_FLAGS " -g")
+endif()
+
+# ${SRC_FILES} is recursively defined in the subdirectories.
+# Each subdirectory only adds the source files that are present at its level.
+add_executable(jabberhive-cli ${SRC_FILES})
+set_property(TARGET jabberhive-cli PROPERTY C_STANDARD 99)
+set_property(TARGET jabberhive-cli PROPERTY C_STANDARD_REQUIRED ON)
+
+## OPTION HANDLING #############################################################
+# TODO
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5d1aa26
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2016, Nathanaƫl Sensfelder
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of Zero of One nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0da4380
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+## What is JabberHive?
+JabberHive is a modular ChatBot system. All "modules" are in fact separate
+programs linked together using the JabberHive Protocol. Please refer to the
+protocol for more information.
+
+## Component Description
+* CLI Gateway for a JabberHive network.
+* Allows the use of the Command Line Interface to send requests using stdin.
+* All replies are displayed in stdout.
+* Handles the lack of pipelining by not reading from stdin if it's not ready to
+send.
+
+## JabberHive Protocol Compatibility
+* **Protocol Version(s):** 1.
+* **Inbound Connections:** None.
+* **Outbound Connections:** Single.
+* **Pipelining:** No.
+* **Behavior:** Gateway.
+
+## Dependencies
+- POSIX compliant OS.
+- C compiler (with C99 support).
+- CMake.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..f99ceed
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_subdirectory(error)
+
+set(
+ SRC_FILES ${SRC_FILES}
+ ${CMAKE_CURRENT_SOURCE_DIR}/main.c
+)
+
+set(SRC_FILES ${SRC_FILES} PARENT_SCOPE)
diff --git a/src/error/CMakeLists.txt b/src/error/CMakeLists.txt
new file mode 100644
index 0000000..fa07534
--- /dev/null
+++ b/src/error/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(
+ SRC_FILES ${SRC_FILES}
+)
+
+set(SRC_FILES ${SRC_FILES} PARENT_SCOPE)
+
diff --git a/src/error/error.h b/src/error/error.h
new file mode 100644
index 0000000..145c838
--- /dev/null
+++ b/src/error/error.h
@@ -0,0 +1,143 @@
+#ifndef _JH_ERROR_ERROR_H_
+#define _JH_ERROR_ERROR_H_
+
+#include <stdio.h>
+
+#include "../pervasive.h"
+
+#ifndef JH_DEBUG_PROGRAM_FLOW
+ #define JH_DEBUG_PROGRAM_FLOW (0 || JH_DEBUG_ALL)
+#endif
+
+#ifndef JH_DEBUG_CONFIG
+ #define JH_DEBUG_CONFIG (0 || JH_DEBUG_ALL)
+#endif
+
+#ifndef JH_DEBUG_LEARNING
+ #define JH_DEBUG_LEARNING (0 || JH_DEBUG_ALL)
+#endif
+
+#ifndef JH_DEBUG_NETWORK
+ #define JH_DEBUG_NETWORK 1
+#endif
+
+#ifndef JH_DEBUG_NETWORK
+ #define JH_DEBUG_NETWORK (0 || JH_DEBUG_ALL)
+#endif
+
+#define JH_ENABLE_WARNINGS_OUTPUT 1
+#define JH_ENABLE_RUNTIME_ERRORS_OUTPUT 1
+#define JH_ENABLE_PROGRAMMING_ERRORS_OUTPUT 1
+#define JH_ENABLE_FATAL_ERROR_OUTPUT 1
+
+#ifdef JH_ENABLE_ERROR_LOCATION
+ #define JH_LOCATION " [" __FILE__ "][" JH_TO_STRING(__LINE__) "]"
+#else
+ #define JH_LOCATION ""
+#endif
+
+#define JH_PRINT_STDERR(io, symbol, str, ...)\
+ fprintf(io, "[" symbol "]" JH_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 JH_DEBUG(io, flag, str, ...)\
+ JH_ISOLATE\
+ (\
+ if (flag)\
+ {\
+ JH_PRINT_STDERR(io, "D", str, __VA_ARGS__);\
+ }\
+ )
+
+
+#define JH_WARNING(io, str, ...)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_WARNINGS_OUTPUT)\
+ {\
+ JH_PRINT_STDERR(io, "W", str, __VA_ARGS__);\
+ }\
+ )
+
+#define JH_ERROR(io, str, ...)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_RUNTIME_ERRORS_OUTPUT)\
+ {\
+ JH_PRINT_STDERR(io, "E", str, __VA_ARGS__);\
+ }\
+ )
+
+#define JH_PROG_ERROR(io, str, ...)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_PROGRAMMING_ERRORS_OUTPUT)\
+ {\
+ JH_PRINT_STDERR(io, "P", str, __VA_ARGS__);\
+ }\
+ )
+
+#define JH_FATAL(io, str, ...)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_FATAL_ERROR_OUTPUT)\
+ {\
+ JH_PRINT_STDERR(io, "F", str, __VA_ARGS__);\
+ }\
+ )
+
+/* For outputs without dynamic content (static). ******************************/
+
+#define JH_PRINT_S_STDERR(io, symbol, str)\
+ fprintf(io, "[" symbol "]" JH_LOCATION " " str "\n");
+
+#define JH_S_DEBUG(io, flag, str)\
+ JH_ISOLATE\
+ (\
+ if (flag)\
+ {\
+ JH_PRINT_S_STDERR(io, "D", str);\
+ }\
+ )
+
+#define JH_S_WARNING(io, str)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_WARNINGS_OUTPUT)\
+ {\
+ JH_PRINT_S_STDERR(io, "W", str);\
+ }\
+ )
+
+#define JH_S_ERROR(io, str)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_RUNTIME_ERRORS_OUTPUT)\
+ {\
+ JH_PRINT_S_STDERR(io, "E", str);\
+ }\
+ )
+
+#define JH_S_PROG_ERROR(io, str)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_PROGRAMMING_ERRORS_OUTPUT)\
+ {\
+ JH_PRINT_S_STDERR(io, "P", str);\
+ }\
+ )
+
+#define JH_S_FATAL(io, str)\
+ JH_ISOLATE\
+ (\
+ if (JH_ENABLE_FATAL_ERROR_OUTPUT)\
+ {\
+ JH_PRINT_S_STDERR(io, "F", str);\
+ }\
+ )
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..9b8be44
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,209 @@
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "error/error.h"
+
+#include "pervasive.h"
+
+static void print_help (const char runnable [])
+{
+ printf
+ (
+ "JabberHive - CLI Gateway\n"
+ "Software Version %d\n"
+ "Protocol Version %d\n"
+ "\nUsages:\n"
+ " JH GATEWAY:\t%s SOCKET_NAME\n"
+ " SHOW HELP:\tAnything else\n"
+ "\nParameters:\n"
+ " SOCKET_NAME: valid UNIX socket.\n",
+ JH_PROGRAM_VERSION,
+ JH_PROTOCOL_VERSION,
+ runnable
+ );
+}
+
+static int open_socket
+(
+ FILE * s [const restrict static 1],
+ const char socket_name [const restrict static 1]
+)
+{
+ int fd;
+ struct sockaddr_un addr;
+
+ const int old_errno = errno;
+
+ errno = 0;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ JH_FATAL
+ (
+ stderr,
+ "Unable to create socket: %s.",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ return -1;
+ }
+
+ errno = old_errno;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path)-1);
+
+ errno = 0;
+
+ if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
+ {
+ JH_FATAL
+ (
+ stderr,
+ "Unable to connect to address: %s.",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ close(fd);
+
+ return -1;
+ }
+
+ errno = 0;
+
+ *s = fdopen(fd, "w+");
+
+ if (*s == (FILE *) NULL)
+ {
+ JH_FATAL
+ (
+ stderr,
+ "Unable to open socket as a file: %s.",
+ strerror(errno)
+ );
+
+ errno = old_errno;
+
+ close(fd);
+
+ return -1;
+ }
+
+ errno = old_errno;
+
+ return 0;
+}
+
+static void gateway
+(
+ FILE socket [const restrict static 1]
+)
+{
+ int is_sending;
+ int data, data_prev, is_last_message;
+
+ is_sending = 1;
+
+ for (;;)
+ {
+ if (is_sending)
+ {
+ data = fgetc(stdin);
+
+ if (data == EOF)
+ {
+ return;
+ }
+ else
+ {
+ if (fputc(data, socket) == EOF)
+ {
+ JH_S_FATAL(stderr, "Unable to write to socket.");
+
+ return;
+ }
+ }
+
+ if (data == '\n')
+ {
+ is_sending = 0;
+ data = 0;
+ is_last_message = 0;
+ }
+ }
+ else
+ {
+ data_prev = data;
+ data = fgetc(socket);
+
+ if (data == EOF)
+ {
+ JH_S_FATAL(stderr, "Unable to read from socket.");
+
+ return;
+ }
+ else
+ {
+ if (fputc(data, stdout) == EOF)
+ {
+ /* ... seems like it wouldn't work. */
+ JH_S_FATAL(stderr, "Unable to write to stdout.");
+
+ return;
+ }
+ }
+
+ is_last_message =
+ (
+ is_last_message
+ || (
+ (data_prev == '!')
+ && (
+ (data == 'P')
+ || (data == 'N')
+ )
+ // TODO: check for the ' '.
+ )
+ );
+
+ if (is_last_message && (data == '\n'))
+ {
+ is_sending = 1;
+ }
+ }
+ }
+}
+
+int main (int const argc, const char * argv [const static argc])
+{
+ FILE * socket;
+
+ if (argc != 2)
+ {
+ print_help(argv[0]);
+
+ return -1;
+ }
+
+ if (open_socket(&socket, argv[1]) < 0)
+ {
+ return -1;
+ }
+
+ gateway(socket);
+
+ fclose(socket);
+
+ return 0;
+}
diff --git a/src/pervasive.h b/src/pervasive.h
new file mode 100644
index 0000000..27d832d
--- /dev/null
+++ b/src/pervasive.h
@@ -0,0 +1,28 @@
+#ifndef _JH_PERVASIVE_H_
+#define _JH_PERVASIVE_H_
+
+#include <string.h>
+
+#define JH_PROGRAM_VERSION 1
+#define JH_PROTOCOL_VERSION 1
+
+#ifdef __FRAMA_C__
+ #define JH_RUNNING_FRAMA_C 1
+#endif
+
+#define JH_DEBUG_ALL 1
+
+#ifndef JH_DEBUG_ALL
+ #define JH_DEBUG_ALL 0
+#endif
+
+#define JH__TO_STRING(x) #x
+#define JH_TO_STRING(x) JH__TO_STRING(x)
+#define JH_ISOLATE(a) do {a} while (0)
+
+/* strncmp stops at '\0' and strlen does not count '\0'. */
+#define JH_IS_PREFIX(a, b) (strncmp(a, b, strlen(a)) == 0)
+
+#define JH_STRING_EQUALS(a, b) (strcmp(a, b) == 0)
+
+#endif