| summaryrefslogtreecommitdiff |
diff options
| -rw-r--r-- | CMakeLists.txt | 24 | ||||
| -rw-r--r-- | LICENSE | 27 | ||||
| -rw-r--r-- | README.md | 23 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/error/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/error/error.h | 143 | ||||
| -rw-r--r-- | src/main.c | 209 | ||||
| -rw-r--r-- | src/pervasive.h | 28 |
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 @@ -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 |


