From 829c988414a1c3835b5d2481e0d8e3009552a295 Mon Sep 17 00:00:00 2001 From: Marc Ransome Date: Sat, 16 Mar 2024 13:32:58 +0000 Subject: [PATCH 1/6] Refactor error handling logic --- src/CMakeLists.txt | 2 +- src/config.c | 41 +++++++++++++++++--------------- src/config.h | 28 ++++++++++++---------- src/defs.h | 36 ---------------------------- src/flog.c | 38 ++++++++++++++++-------------- src/flog.h | 16 +++++++++---- src/main.c | 48 +++++++++++++++++++++++--------------- src/utils.c | 58 ---------------------------------------------- src/utils.h | 37 ----------------------------- test/test_config.c | 57 +++++++++++++++++++++++++++++++-------------- 10 files changed, 141 insertions(+), 220 deletions(-) delete mode 100644 src/defs.h delete mode 100644 src/utils.c delete mode 100644 src/utils.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14c9213..dcbc635 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ set(target flog) -add_executable(flog main.c flog.c flog.h config.c config.h defs.h utils.c utils.h) +add_executable(flog main.c flog.c flog.h config.c config.h common.h common.c common.h) target_link_libraries(${target} PRIVATE ${POPT_LINK_LIBRARIES}) target_include_directories(${target} PRIVATE ${POPT_INCLUDE_DIRS}) diff --git a/src/config.c b/src/config.c index aea54b7..3360458 100644 --- a/src/config.c +++ b/src/config.c @@ -21,10 +21,9 @@ // SOFTWARE. #include "config.h" -#include "defs.h" +#include "common.h" #include #include -#include #include #include #include @@ -70,22 +69,20 @@ struct FlogConfigData { }; FlogConfig * -flog_config_new(int argc, char *argv[]) { +flog_config_new(int argc, char *argv[], FlogError *error) { assert(argc > 0); assert(argv != NULL); + assert(error != NULL); - if (argc == 1) { - errno = ERR_NO_ARGUMENTS_PROVIDED; - return NULL; - } + *error = FLOG_ERROR_NONE; FlogConfig *config = calloc(1, sizeof(struct FlogConfigData)); if (config == NULL) { - errno = ERR_CONFIG_ALLOCATION; + *error = FLOG_ERROR_ALLOC; return NULL; } - flog_config_set_level(config, Default); + flog_config_set_level(config, LVL_DEFAULT); flog_config_set_message_type(config, Public); flog_config_set_version_flag(config, false); flog_config_set_help_flag(config, false); @@ -111,9 +108,16 @@ flog_config_new(int argc, char *argv[]) { break; case 'l': flog_config_set_level(config, flog_config_parse_level(option_argument)); + if (flog_config_get_level(config) == LVL_UNKNOWN) { + flog_config_free(config); + poptFreeContext(context); + *error = FLOG_ERROR_LVL; + return NULL; + } break; case 's': flog_config_set_subsystem(config, option_argument); + break; case 'c': flog_config_set_category(config, option_argument); @@ -134,15 +138,14 @@ flog_config_new(int argc, char *argv[]) { flog_config_free(config); poptFreeContext(context); - errno = ERR_PROGRAM_OPTIONS; + *error = FLOG_ERROR_OPTS; return NULL; } if (strlen(flog_config_get_category(config)) > 0 && strlen(flog_config_get_subsystem(config)) == 0) { - fprintf(stderr, "%s: category option requires subsystem option\n", PROGRAM_NAME); flog_config_free(config); poptFreeContext(context); - errno = ERR_CATEGORY_OPTION_REQUIRES_SUBSYSTEM; + *error = FLOG_ERROR_SUBSYS; return NULL; } @@ -151,7 +154,7 @@ flog_config_new(int argc, char *argv[]) { if (message_args == NULL) { flog_config_free(config); poptFreeContext(context); - errno = ERR_NO_MESSAGE_STRING_PROVIDED; + *error = FLOG_ERROR_MSG; return NULL; } @@ -243,17 +246,17 @@ flog_config_parse_level(const char *str) { FlogConfigLevel level; if (strcmp(str, "default") == 0) { - level = Default; + level = LVL_DEFAULT; } else if (strcmp(str, "info") == 0) { - level = Info; + level = LVL_INFO; } else if (strcmp(str, "debug") == 0) { - level = Debug; + level = LVL_DEBUG; } else if (strcmp(str, "error") == 0) { - level = Error; + level = LVL_ERROR; } else if (strcmp(str, "fault") == 0) { - level = Fault; + level = LVL_FAULT; } else { - level = Unknown; + level = LVL_UNKNOWN; } return level; diff --git a/src/config.h b/src/config.h index 8b45b05..c71e2d7 100644 --- a/src/config.h +++ b/src/config.h @@ -31,15 +31,16 @@ #include #include #include +#include "common.h" /*! \brief An enumerated type representing the log level. */ typedef enum FlogConfigLevelData { - Default, - Info, - Debug, - Error, - Fault, - Unknown + LVL_DEFAULT, + LVL_INFO, + LVL_DEBUG, + LVL_ERROR, + LVL_FAULT, + LVL_UNKNOWN } FlogConfigLevel; /*! \brief An enumerated type representing the log message type. */ @@ -57,18 +58,21 @@ typedef struct FlogConfigData FlogConfig; /*! \brief Create a FlogConfig object representing configuration to be used * with a FlogCli logger object. * - * \param argc An integer representing the number of command-line arguments - * \param argv A pointer to an array of null-terminated strings representing - * command-line arguments + * \param[in] argc An integer representing the number of command-line arguments + * \param[in] argv A pointer to an array of null-terminated strings representing + * command-line arguments + * \param[out] error A pointer to a FlogError object that will be used to represent + * an error condition on failure * * \pre \c argc is greater than \c 0 * \pre \c argv is \e not \c NULL + * \pre \c error is \e not \c NULL * * \return If successful, a pointer to a FlogConfig object; if there is an error - * a \c NULL pointer is returned and \c errno will be set to one of several - * potential error values (see defs.h) + * a \c NULL pointer is returned and \c error will be set to a FlogError + * variant representing an error condition */ -FlogConfig * flog_config_new(int argc, char *argv[]); +FlogConfig * flog_config_new(int argc, char *argv[], FlogError *error); /*! \brief Free a FlogConfig object. * diff --git a/src/defs.h b/src/defs.h deleted file mode 100644 index a65b98b..0000000 --- a/src/defs.h +++ /dev/null @@ -1,36 +0,0 @@ -// MIT License -// -// Copyright (c) 2022 Marc Ransome -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef FLOG_DEFS_H -#define FLOG_DEFS_H - -#define PROGRAM_NAME "flog" -#define PROGRAM_VERSION "1.4.0" - -#define ERR_CATEGORY_OPTION_REQUIRES_SUBSYSTEM 1 -#define ERR_NO_MESSAGE_STRING_PROVIDED 2 -#define ERR_NO_ARGUMENTS_PROVIDED 3 -#define ERR_PROGRAM_OPTIONS 4 -#define ERR_CONFIG_ALLOCATION 5 -#define ERR_FLOG_ALLOCATION 6 - -#endif //FLOG_DEFS_H diff --git a/src/flog.c b/src/flog.c index 76ce9e2..2d09797 100644 --- a/src/flog.c +++ b/src/flog.c @@ -22,11 +22,10 @@ #include "flog.h" #include -#include #include #include #include -#include "defs.h" +#include "common.h" #include "config.h" #ifdef UNIT_TESTING @@ -50,12 +49,15 @@ struct FlogCliData { }; FlogCli * -flog_cli_new(FlogConfig *config) { +flog_cli_new(FlogConfig *config, FlogError *error) { assert(config != NULL); + assert(error != NULL); + + *error = FLOG_ERROR_NONE; FlogCli *flog = calloc(1, sizeof(struct FlogCliData)); if (flog == NULL) { - errno = ERR_FLOG_ALLOCATION; + *error = FLOG_ERROR_ALLOC; return NULL; } @@ -109,7 +111,7 @@ flog_commit_message(FlogCli *flog) { } } -void +FlogError flog_append_message_output(FlogCli *flog) { assert(flog != NULL); @@ -122,8 +124,8 @@ flog_append_message_output(FlogCli *flog) { FILE *fd = fopen(output_file, "a"); if (fd == NULL) { - fprintf(stderr, "%s: unable to append log message to file (%s)\n", PROGRAM_NAME, strerror(errno)); - exit(errno); + umask(original_umask); + return FLOG_ERROR_APPEND; } umask(original_umask); @@ -131,6 +133,8 @@ flog_append_message_output(FlogCli *flog) { fprintf(fd, "%s", flog_config_get_message(config)); fclose(fd); } + + return FLOG_ERROR_NONE; } void @@ -142,19 +146,19 @@ flog_commit_public_message(FlogCli *flog) { const char *message = flog_config_get_message(config); switch (level) { - case Default: + case LVL_DEFAULT: os_log(flog->log, OS_LOG_FORMAT_PUBLIC, message); break; - case Info: + case LVL_INFO: os_log_info(flog->log, OS_LOG_FORMAT_PUBLIC, message); break; - case Debug: + case LVL_DEBUG: os_log_debug(flog->log, OS_LOG_FORMAT_PUBLIC, message); break; - case Error: + case LVL_ERROR: os_log_error(flog->log, OS_LOG_FORMAT_PUBLIC, message); break; - case Fault: + case LVL_FAULT: os_log_fault(flog->log, OS_LOG_FORMAT_PUBLIC, message); break; default: @@ -172,19 +176,19 @@ flog_commit_private_message(FlogCli *flog) { const char *message = flog_config_get_message(config); switch (level) { - case Default: + case LVL_DEFAULT: os_log(flog->log, OS_LOG_FORMAT_PRIVATE, message); break; - case Info: + case LVL_INFO: os_log_info(flog->log, OS_LOG_FORMAT_PRIVATE, message); break; - case Debug: + case LVL_DEBUG: os_log_debug(flog->log, OS_LOG_FORMAT_PRIVATE, message); break; - case Error: + case LVL_ERROR: os_log_error(flog->log, OS_LOG_FORMAT_PRIVATE, message); break; - case Fault: + case LVL_FAULT: os_log_fault(flog->log, OS_LOG_FORMAT_PRIVATE, message); break; default: diff --git a/src/flog.h b/src/flog.h index b1f27c5..7b7670e 100644 --- a/src/flog.h +++ b/src/flog.h @@ -24,6 +24,7 @@ #define FLOG_H #include "config.h" +#include "common.h" /*! \file flog.h * @@ -38,14 +39,18 @@ typedef struct FlogCliData FlogCli; /*! \brief Create a FlogCli object to be used for logging messages to the unified logging system. * - * \param config A pointer to a FlogConfig object + * \param[in] config A pointer to a FlogConfig object + * \param[out] error A pointer to a FlogError object that will be used to represent + * an error condition on failure * * \pre \c config is \e not \c NULL + * \pre \c error is \e not \c NULL * * \return If successful, a pointer to a FlogCli object; if there is an error - * a \c NULL pointer is returned and \c errno will be set to \c ERR_FLOG_ALLOCATION + * a \c NULL pointer is returned and \c error will be set to a FlogError + * variant representing an error condition */ -FlogCli * flog_cli_new(FlogConfig *config); +FlogCli * flog_cli_new(FlogConfig *config, FlogError *error); /*! \brief Free a FlogCli object. * @@ -88,7 +93,10 @@ void flog_commit_message(FlogCli *flog); * \param flog A pointer to the FlogCli object * * \pre \c flog is \e not \c NULL + * + * \return If successful, the FlogError variant FLOG_ERROR_NONE, otherwise some + * other variant representing an error condition */ -void flog_append_message_output(FlogCli *flog); +FlogError flog_append_message_output(FlogCli *flog); #endif //FLOG_H diff --git a/src/main.c b/src/main.c index c4e23db..3e63ac5 100644 --- a/src/main.c +++ b/src/main.c @@ -22,44 +22,54 @@ #include #include -#include #include "flog.h" -#include "defs.h" -#include "utils.h" +#include "common.h" int main(int argc, char *argv[]) { - FlogConfig *config = flog_config_new(argc, argv); + + if (argc == 1 ) { + flog_usage(); + return 1; + } + + FlogError error = FLOG_ERROR_NONE; + FlogConfig *config = flog_config_new(argc, argv, &error); if (config == NULL) { - if (errno == ERR_CONFIG_ALLOCATION) { - fprintf(stderr, "%s: config allocation failure\n", PROGRAM_NAME); - } else if (errno == ERR_NO_ARGUMENTS_PROVIDED) { - flog_usage(); - } else if (errno == ERR_NO_MESSAGE_STRING_PROVIDED) { - fprintf(stderr, "%s: message string required\n", PROGRAM_NAME); + if (error != FLOG_ERROR_OPTS) { + flog_print_error(error); } - return errno; + return error; } if (flog_config_get_version_flag(config)) { + flog_config_free(config); flog_version(); return EXIT_SUCCESS; } else if (flog_config_get_help_flag(config)) { + flog_config_free(config); flog_usage(); return EXIT_SUCCESS; - } else if (flog_config_get_level(config) == Unknown) { - fprintf(stderr, "%s: unknown log level\n", PROGRAM_NAME); - return EXIT_FAILURE; } - FlogCli *flog = flog_cli_new(config); - if (flog == NULL && errno == ERR_FLOG_ALLOCATION) { - fprintf(stderr, "%s: logger allocation failure\n", PROGRAM_NAME); - return errno; + error = FLOG_ERROR_NONE; + FlogCli *flog = flog_cli_new(config, &error); + if (flog == NULL) { + flog_config_free(config); + flog_print_error(error); + return error; + } + + error = FLOG_ERROR_NONE; + error = flog_append_message_output(flog); + if (error != FLOG_ERROR_NONE) { + flog_cli_free(flog); + flog_config_free(config); + flog_print_error(error); + return error; } flog_commit_message(flog); - flog_append_message_output(flog); flog_cli_free(flog); flog_config_free(config); diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 27f40d3..0000000 --- a/src/utils.c +++ /dev/null @@ -1,58 +0,0 @@ -// MIT License -// -// Copyright (c) 2022 Marc Ransome -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "utils.h" -#include "defs.h" -#include - -void -flog_usage(void) { - printf( - "%s %s\n" - "\n" - "Usage:\n" - " %s [options] message\n" - "\n" - "Help Options:\n" - " -h, --help Show this help message\n" - " -v, --version Print the version string\n" - "\n" - "Application Options:\n" - " -s, --subsystem Specify a subsystem name\n" - " -c, --category Specify a category name (requires subsystem option)\n" - " -l, --level Specify the log level ('default' if not provided)\n" - " -a, --append Append the log message to a file (creating it if necessary)\n" - " -p, --private Mark the log message as private\n" - "\n" - "Log Levels:\n" - " default, info, debug, error, fault\n" - "\n", - PROGRAM_NAME, - PROGRAM_VERSION, - PROGRAM_NAME - ); -} - -void -flog_version(void) { - printf("%s %s\n", PROGRAM_NAME, PROGRAM_VERSION); -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 54ad3f8..0000000 --- a/src/utils.h +++ /dev/null @@ -1,37 +0,0 @@ -// MIT License -// -// Copyright (c) 2022 Marc Ransome -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef FLOG_UTILS_H -#define FLOG_UTILS_H - -/*! \file utils.h - * - * General helper functions for common operations. - */ - -/*! \brief Print help information. */ -void flog_usage(void); - -/*! \brief Print the version string. */ -void flog_version(void); - -#endif //FLOG_UTILS_H diff --git a/test/test_config.c b/test/test_config.c index 7c85571..d3ef78a 100644 --- a/test/test_config.c +++ b/test/test_config.c @@ -24,9 +24,8 @@ #include #include #include -#include #include "config.h" -#include "defs.h" +#include "common.h" #define TEST_PROGRAM_NAME "flog" #define TEST_MESSAGE "test message" @@ -38,110 +37,134 @@ #define TEST_OPTION_SUBSYSTEM_SHORT "-s" #define TEST_OPTION_SUBSYSTEM_LONG "--subsystem" +#define TEST_ERROR 255 + void flog_usage() {} void flog_version() {} static void test_new_config_with_zero_arg_count_calls_assert(void **state) { + FlogError error; int mock_argc = 0; // should fail >0 assertion char *mock_argv[] = {}; // should pass non-null assertion - expect_assert_failure(flog_config_new(mock_argc, mock_argv)); + expect_assert_failure(flog_config_new(mock_argc, mock_argv, &error)); } static void test_new_config_with_null_arg_values_calls_assert(void **state) { + FlogError error; int mock_argc = 1; // should pass >0 assertion char **mock_argv = NULL; // should fail non-null assertion - expect_assert_failure(flog_config_new(mock_argc, mock_argv)); + expect_assert_failure(flog_config_new(mock_argc, mock_argv, &error)); +} + +static void test_new_config_with_no_error_ptr_calls_assert(void **state) { + int mock_argc = 1; // should pass >0 assertion + char *mock_argv[] = {}; // should pass non-null assertion + + expect_assert_failure(flog_config_new(mock_argc, mock_argv, NULL)); } static void test_new_config_returns_non_null(void **state) { + FlogError error = TEST_ERROR; int mock_argc = 2; char *mock_argv[] = { TEST_PROGRAM_NAME, TEST_MESSAGE }; - FlogConfig *config = flog_config_new(mock_argc, mock_argv); + FlogConfig *config = flog_config_new(mock_argc, mock_argv, &error); assert_non_null(config); + assert_int_equal(error, FLOG_ERROR_NONE); flog_config_free(config); } static void test_new_config_with_short_version_option(void **state) { + FlogError error = TEST_ERROR; int mock_argc = 2; char *mock_argv[] = { TEST_PROGRAM_NAME, TEST_OPTION_VERSION_SHORT }; - FlogConfig *config = flog_config_new(mock_argc, mock_argv); + + FlogConfig *config = flog_config_new(mock_argc, mock_argv, &error); assert_true(flog_config_get_version_flag(config)); + assert_int_equal(error, FLOG_ERROR_NONE); flog_config_free(config); } static void test_new_config_with_long_version_option(void **state) { + FlogError error = TEST_ERROR; int mock_argc = 2; char *mock_argv[] = { TEST_PROGRAM_NAME, TEST_OPTION_VERSION_LONG }; - FlogConfig *config = flog_config_new(mock_argc, mock_argv); + FlogConfig *config = flog_config_new(mock_argc, mock_argv, &error); assert_true(flog_config_get_version_flag(config)); + assert_int_equal(error, FLOG_ERROR_NONE); flog_config_free(config); } static void test_new_config_with_short_help_option(void **state) { + FlogError error = TEST_ERROR; int mock_argc = 2; char *mock_argv[] = { TEST_PROGRAM_NAME, TEST_OPTION_HELP_SHORT }; - FlogConfig *config = flog_config_new(mock_argc, mock_argv); + FlogConfig *config = flog_config_new(mock_argc, mock_argv, &error); assert_true(flog_config_get_help_flag(config)); + assert_int_equal(error, FLOG_ERROR_NONE); flog_config_free(config); } static void test_new_config_with_long_help_option(void **state) { + FlogError error = TEST_ERROR; int mock_argc = 2; char *mock_argv[] = { TEST_PROGRAM_NAME, TEST_OPTION_HELP_LONG }; - FlogConfig *config = flog_config_new(mock_argc, mock_argv); + FlogConfig *config = flog_config_new(mock_argc, mock_argv, &error); assert_true(flog_config_get_help_flag(config)); + assert_int_equal(error, FLOG_ERROR_NONE); flog_config_free(config); } -static void test_new_config_with_short_subsystem_option_returns_null_and_sets_errno(void **state) { +static void test_new_config_with_short_subsystem_option_returns_null_and_sets_error(void **state) { + FlogError error = TEST_ERROR; int mock_argc = 2; char *mock_argv[] = { TEST_PROGRAM_NAME, TEST_OPTION_SUBSYSTEM_SHORT }; - FlogConfig *config = flog_config_new(mock_argc, mock_argv); + FlogConfig *config = flog_config_new(mock_argc, mock_argv, &error); assert_null(config); - assert_int_equal(errno, ERR_PROGRAM_OPTIONS); + assert_int_equal(error, FLOG_ERROR_OPTS); } -static void test_new_config_with_long_subsystem_option_returns_null_and_sets_errno(void **state) { +static void test_new_config_with_long_subsystem_option_returns_null_and_sets_error(void **state) { + FlogError error = TEST_ERROR; int mock_argc = 2; char *mock_argv[] = { TEST_PROGRAM_NAME, TEST_OPTION_SUBSYSTEM_LONG }; - FlogConfig *config = flog_config_new(mock_argc, mock_argv); + FlogConfig *config = flog_config_new(mock_argc, mock_argv, &error); assert_null(config); - assert_int_equal(errno, ERR_PROGRAM_OPTIONS); + assert_int_equal(error, FLOG_ERROR_OPTS); } int main(void) { @@ -155,8 +178,8 @@ int main(void) { cmocka_unit_test(test_new_config_with_long_version_option), cmocka_unit_test(test_new_config_with_short_help_option), cmocka_unit_test(test_new_config_with_long_help_option), - cmocka_unit_test(test_new_config_with_short_subsystem_option_returns_null_and_sets_errno), - cmocka_unit_test(test_new_config_with_long_subsystem_option_returns_null_and_sets_errno), + cmocka_unit_test(test_new_config_with_short_subsystem_option_returns_null_and_sets_error), + cmocka_unit_test(test_new_config_with_long_subsystem_option_returns_null_and_sets_error), }; return cmocka_run_group_tests(tests, NULL, NULL); From ea5c46dd60c542d84b3932d925d5d6685c751911 Mon Sep 17 00:00:00 2001 From: Marc Ransome Date: Sat, 16 Mar 2024 13:34:32 +0000 Subject: [PATCH 2/6] Remove duplicate header from build configuration --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dcbc635..e80dcf4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ set(target flog) -add_executable(flog main.c flog.c flog.h config.c config.h common.h common.c common.h) +add_executable(flog main.c flog.c flog.h config.c config.h common.h common.c) target_link_libraries(${target} PRIVATE ${POPT_LINK_LIBRARIES}) target_include_directories(${target} PRIVATE ${POPT_INCLUDE_DIRS}) From 0d2ca5ff74f7cf376f72f5c9fb72484a6e0e512b Mon Sep 17 00:00:00 2001 From: Marc Ransome Date: Sat, 16 Mar 2024 13:41:24 +0000 Subject: [PATCH 3/6] Add missing common files --- src/common.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/common.h | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 src/common.c create mode 100644 src/common.h diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..c9116f0 --- /dev/null +++ b/src/common.c @@ -0,0 +1,83 @@ +// MIT License +// +// Copyright (c) 2022 Marc Ransome +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "common.h" +#include + +static const char * +flog_error_map[] = { + [FLOG_ERROR_NONE] = "none", + [FLOG_ERROR_ALLOC] = "allocation failed", + [FLOG_ERROR_APPEND] = "unable to append log message to file", + [FLOG_ERROR_LVL] = "unknown log level", + [FLOG_ERROR_MSG] = "message string required", + [FLOG_ERROR_SUBSYS] = "category option requires subsystem option to be set", + [FLOG_ERROR_OPTS] = "invalid options", +}; + +const char * +flog_error_string(FlogError error) { + return flog_error_map[error]; +} + +void +flog_print_error(FlogError error) { + fprintf(stderr, "%s: %s\n", PROGRAM_NAME, flog_error_string(error)); +} + +void +flog_print_error_string(const char *error) { + fprintf(stderr, "%s: %s", PROGRAM_NAME, error); +} + +void +flog_usage(void) { + printf( + "%s %s\n" + "\n" + "Usage:\n" + " %s [options] message\n" + "\n" + "Help Options:\n" + " -h, --help Show this help message\n" + " -v, --version Print the version string\n" + "\n" + "Application Options:\n" + " -s, --subsystem Specify a subsystem name\n" + " -c, --category Specify a category name (requires subsystem option)\n" + " -l, --level Specify the log level ('default' if not provided)\n" + " -a, --append Append the log message to a file (creating it if necessary)\n" + " -p, --private Mark the log message as private\n" + "\n" + "Log Levels:\n" + " default, info, debug, error, fault\n" + "\n", + PROGRAM_NAME, + PROGRAM_VERSION, + PROGRAM_NAME + ); +} + +void +flog_version(void) { + printf("%s %s\n", PROGRAM_NAME, PROGRAM_VERSION); +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..eab7861 --- /dev/null +++ b/src/common.h @@ -0,0 +1,75 @@ +// MIT License +// +// Copyright (c) 2022 Marc Ransome +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef FLOG_COMMON_H +#define FLOG_COMMON_H + +/*! \file common.h + * + * Constants, helper functions, and error handling types for common operations. + */ + +#define PROGRAM_NAME "flog" +#define PROGRAM_VERSION "1.4.0" + +/*! \brief An enumerated type representing error conditions. */ +typedef enum FlogErrorData { + FLOG_ERROR_NONE, + FLOG_ERROR_ALLOC, + FLOG_ERROR_APPEND, + FLOG_ERROR_LVL, + FLOG_ERROR_MSG, + FLOG_ERROR_OPTS, + FLOG_ERROR_SUBSYS, +} FlogError; + +/*! \brief Print usage information to stdout stream. */ +void flog_usage(void); + +/*! \brief Print version string to stdout stream. */ +void flog_version(void); + +/*! \brief Return a string representation of an error condition. + * + * \param[in] error A \c FlogError enumeration variant + * + * \return A pointer to a null-terminated string describing the error condition + */ +const char * flog_get_error_string(FlogError error); + +/*! \brief Print a formatted error message representation of the error condition. + * + * \param[in] error A \c FlogError enumeration variant representing the error condition + * + * \return void + */ +void flog_print_error(FlogError error); + +/*! \brief Print a formatted error message string. + * + * \param[out] error A null-terminated string describing the error condition. + * + * \return void + */ +void flog_print_error_string(const char *error); + +#endif //FLOG_COMMON_H From efb21523dd6c6372e1dfdbf43c0285972848f414 Mon Sep 17 00:00:00 2001 From: Marc Ransome Date: Sat, 16 Mar 2024 13:43:34 +0000 Subject: [PATCH 4/6] Fix indentation --- src/common.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/common.c b/src/common.c index c9116f0..a09bea3 100644 --- a/src/common.c +++ b/src/common.c @@ -25,18 +25,18 @@ static const char * flog_error_map[] = { - [FLOG_ERROR_NONE] = "none", - [FLOG_ERROR_ALLOC] = "allocation failed", - [FLOG_ERROR_APPEND] = "unable to append log message to file", - [FLOG_ERROR_LVL] = "unknown log level", - [FLOG_ERROR_MSG] = "message string required", - [FLOG_ERROR_SUBSYS] = "category option requires subsystem option to be set", - [FLOG_ERROR_OPTS] = "invalid options", + [FLOG_ERROR_NONE] = "none", + [FLOG_ERROR_ALLOC] = "allocation failed", + [FLOG_ERROR_APPEND] = "unable to append log message to file", + [FLOG_ERROR_LVL] = "unknown log level", + [FLOG_ERROR_MSG] = "message string required", + [FLOG_ERROR_SUBSYS] = "category option requires subsystem option to be set", + [FLOG_ERROR_OPTS] = "invalid options", }; const char * flog_error_string(FlogError error) { - return flog_error_map[error]; + return flog_error_map[error]; } void From 2438f9e9ea3ad954a245d82e9740276cef7e337e Mon Sep 17 00:00:00 2001 From: Marc Ransome Date: Sat, 16 Mar 2024 13:46:11 +0000 Subject: [PATCH 5/6] Remove unused common function --- src/common.c | 5 ----- src/common.h | 8 -------- 2 files changed, 13 deletions(-) diff --git a/src/common.c b/src/common.c index a09bea3..50fa9d5 100644 --- a/src/common.c +++ b/src/common.c @@ -44,11 +44,6 @@ flog_print_error(FlogError error) { fprintf(stderr, "%s: %s\n", PROGRAM_NAME, flog_error_string(error)); } -void -flog_print_error_string(const char *error) { - fprintf(stderr, "%s: %s", PROGRAM_NAME, error); -} - void flog_usage(void) { printf( diff --git a/src/common.h b/src/common.h index eab7861..a73e4ca 100644 --- a/src/common.h +++ b/src/common.h @@ -64,12 +64,4 @@ const char * flog_get_error_string(FlogError error); */ void flog_print_error(FlogError error); -/*! \brief Print a formatted error message string. - * - * \param[out] error A null-terminated string describing the error condition. - * - * \return void - */ -void flog_print_error_string(const char *error); - #endif //FLOG_COMMON_H From 9717a3d52fcf29be399d41a7c6df85aacad7d2c9 Mon Sep 17 00:00:00 2001 From: Marc Ransome Date: Sat, 16 Mar 2024 16:53:19 +0000 Subject: [PATCH 6/6] Update documentation comments --- src/common.h | 4 ++-- test/test_config.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common.h b/src/common.h index a73e4ca..acc4c96 100644 --- a/src/common.h +++ b/src/common.h @@ -50,7 +50,7 @@ void flog_version(void); /*! \brief Return a string representation of an error condition. * - * \param[in] error A \c FlogError enumeration variant + * \param[in] error A FlogError enumeration variant * * \return A pointer to a null-terminated string describing the error condition */ @@ -58,7 +58,7 @@ const char * flog_get_error_string(FlogError error); /*! \brief Print a formatted error message representation of the error condition. * - * \param[in] error A \c FlogError enumeration variant representing the error condition + * \param[in] error A FlogError enumeration variant representing the error condition * * \return void */ diff --git a/test/test_config.c b/test/test_config.c index d3ef78a..16d07ad 100644 --- a/test/test_config.c +++ b/test/test_config.c @@ -43,7 +43,7 @@ void flog_usage() {} void flog_version() {} static void test_new_config_with_zero_arg_count_calls_assert(void **state) { - FlogError error; + FlogError error; // should pass non-null assertion (&error) int mock_argc = 0; // should fail >0 assertion char *mock_argv[] = {}; // should pass non-null assertion @@ -51,7 +51,7 @@ static void test_new_config_with_zero_arg_count_calls_assert(void **state) { } static void test_new_config_with_null_arg_values_calls_assert(void **state) { - FlogError error; + FlogError error; // should pass non-null assertion (&error) int mock_argc = 1; // should pass >0 assertion char **mock_argv = NULL; // should fail non-null assertion