diff --git a/src/io_backend/io_backend.c b/src/io_backend/io_backend.c index 176585f..97fe974 100644 --- a/src/io_backend/io_backend.c +++ b/src/io_backend/io_backend.c @@ -1,35 +1,102 @@ #include "io_backend.h" #include +#include +#include + +// === Static variables static struct iob_context context; -static FILE *input; +static FILE *input = NULL; +static char *stream_buf = NULL; +static size_t stream_buf_size = 0; +static enum iob_state state = IOB_STATE_NOT_INITIALIZED; + +// === Functions int iob_init(struct iob_context *ctx) { + if (state != IOB_STATE_NOT_INITIALIZED) + return IOB_ERROR_MODULE_ALREADY_INITIALIZED; + context = *ctx; switch (context.mode) { case IOB_MODE_STDIN: input = stdin; + state = IOB_STATE_READY; return 0; case IOB_MODE_SCRIPT: if (context.args == NULL) - return -2; + return IOB_ERROR_BAD_ARG; input = fopen(context.args, "r"); if (input == NULL) - return -4; + return IOB_ERROR_CANNOT_OPEN_FILE; + state = IOB_STATE_READY; return 0; case IOB_MODE_CMD: if (context.args != NULL) - return -2; - else - return 0; + return IOB_ERROR_BAD_ARG; + state = IOB_STATE_READY; + return 0; default: - return -1; + return IOB_ERROR_BAD_ARG; + } +} + +void iob_close(void) +{ + fclose(input); + if ((context.mode == IOB_MODE_STDIN || context.mode == IOB_MODE_SCRIPT) + && stream_buf != NULL) + { + free(stream_buf); + stream_buf_size = 0; + } + state = IOB_STATE_NOT_INITIALIZED; +} + +ssize_t stream_read(char **stream) +{ + // Check args + if (stream == NULL) + return IOB_ERROR_BAD_ARG; + + // Check env + if (state == IOB_STATE_NOT_INITIALIZED) + return IOB_ERROR_MODULE_NOT_INITIALIZED; + if (state == IOB_STATE_FINISHED) + return 0; + if (state == IOB_STATE_ERROR) + return IOB_ERROR_GENERIC; + + // Use input + if (context.mode == IOB_MODE_STDIN || context.mode == IOB_MODE_SCRIPT) + { + ssize_t nread = getline(&stream_buf, &stream_buf_size, input); + if (nread == -1) + { + state = IOB_STATE_FINISHED; + return 0; + } + else if (nread < 0) + state = IOB_STATE_ERROR; + + return nread; + } + // Use args + else if (context.mode == IOB_MODE_CMD) + { + *stream = context.args; + return strlen(context.args); + } + else + { + *stream = NULL; + return IOB_ERROR_GENERIC; } } diff --git a/src/io_backend/io_backend.h b/src/io_backend/io_backend.h index fab5cce..b8eed52 100644 --- a/src/io_backend/io_backend.h +++ b/src/io_backend/io_backend.h @@ -3,6 +3,13 @@ #include +// Error codes +#define IOB_ERROR_GENERIC -1 +#define IOB_ERROR_BAD_ARG -2 +#define IOB_ERROR_MODULE_NOT_INITIALIZED -3 +#define IOB_ERROR_MODULE_ALREADY_INITIALIZED -4 +#define IOB_ERROR_CANNOT_OPEN_FILE -5 + enum iob_mode { IOB_MODE_NULL = 0, @@ -11,11 +18,19 @@ enum iob_mode IOB_MODE_CMD }; +enum iob_state +{ + IOB_STATE_NOT_INITIALIZED, + IOB_STATE_READY, + IOB_STATE_FINISHED, + IOB_STATE_ERROR +}; + /* @struct iob_context * @var mode * @var args contains - * the script name when mode is set to IOB_SCRIPT, - * the command to execute when mode is set to IOB_CMD, + * the script name when mode is set to IOB_MODE_SCRIPT, + * the command to execute when mode is set to IOB_MODE_CMD */ struct iob_context { @@ -31,17 +46,16 @@ struct iob_context */ int iob_init(struct iob_context *context); -/* TODO - * - * - * +/* @brief Closes the opened buffers and the module gracefully */ -void iob_close(); +void iob_close(void); -/*i TODO - * - * +/* @brief reads at most one line of the input and stores it into *stream * + * @param stream is a pointer that will be set to a string to parse + * @return the number of read characters if positive, + * zero if finished (reached EOF), + * the error code otherwise */ ssize_t stream_read(char **stream); diff --git a/tests/unit/io_backend/io_backend.c b/tests/unit/io_backend/io_backend.c new file mode 100644 index 0000000..47e02db --- /dev/null +++ b/tests/unit/io_backend/io_backend.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include + +TestSuite(IO_Backend); + +// IOB Init + +Test(IO_Backend, init_null) +{ + struct iob_context ctx = + { + .iob_mode = IOB_MODE_NULL; + .args = NULL; +}; +int actual = iob_init(ctx); +int expected = IOB_ERROR_BAD_ARG; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_stdin) +{ + struct iob_context ctx = + { + .iob_mode = IOB_MODE_STDIN; + .args = NULL; +}; +int actual = iob_init(ctx); +int expected = 0; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); +} + +// WARNING: this one could fail because of iob_close in the previous test +// Same applies for other tests +Test(IO_Backend, init_script) +{ + char *script_name = "script.tmp" struct iob_context ctx = { + .iob_mode = IOB_MODE_SCRIPT; + .args = script_name; +}; +// Create file +FILE *f = fopen(script_name, "w"); +fclose(f); + +int actual = iob_init(ctx); +int expected = 0; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); +remove(script_name); +} + +Test(IO_Backend, init_script_not_a_file) +{ + char *script_name = "not_a_file.tmp" struct iob_context ctx = { + .iob_mode = IOB_MODE_SCRIPT; + .args = script_name; +}; +int actual = iob_init(ctx); +int expected = IOB_ERROR_CANNOT_OPEN_FILE; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_script_null) +{ + struct iob_context ctx = + { + .iob_mode = IOB_MODE_SCRIPT; + .args = NULL; +}; +int actual = iob_init(ctx); +int expected = IOB_ERROR_CANNOT_OPEN_FILE; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_cmd) +{ + char *cmd = "iamacommand --yesido" struct iob_context ctx = { + .iob_mode = IOB_MODE_CMD; + .args = cmd; +}; +int actual = iob_init(ctx); +int expected = 0; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); +} + +Test(IO_Backend, init_cmd_null) +{ + struct iob_context ctx = + { + .iob_mode = IOB_MODE_CMD; + .args = NULL; +}; +int actual = iob_init(ctx); +int expected = IOB_ERROR_BAD_ARG; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +} + +Test(IO_Backend, init_already_init) +{ + char *cmd = "iamacommand --yesido" struct iob_context ctx = { + .iob_mode = IOB_MODE_CMD; + .args = cmd; +}; +iob_init(ctx); +int actual = iob_init(ctx); +int expected = IOB_ERROR_ALREADY_INITIALIZED; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); +} + +Test(IO_Backend, close_not_init) +{ + iob_close(); // Shouldn't do anything +} + +// IOB Stream +// TODO