From 1028ef1a74d09020a1d7c1d40d8ff3a8f4825301 Mon Sep 17 00:00:00 2001 From: r00t Date: Mon, 28 Dec 2020 03:33:17 +0100 Subject: [PATCH] add sml_error function pointer, allow user to intercept error messages --- sml/include/sml/sml_shared.h | 10 +++++ sml/src/sml_file.c | 2 +- sml/src/sml_message.c | 8 ++-- sml/src/sml_shared.c | 20 +++++++++ sml/src/sml_transport.c | 6 +-- sml/src/sml_tree.c | 2 +- sml/src/sml_value.c | 2 +- test/Makefile | 3 +- test/src/sml_error_test.c | 78 ++++++++++++++++++++++++++++++++++++ test/test_main.c | 1 + 10 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 test/src/sml_error_test.c diff --git a/sml/include/sml/sml_shared.h b/sml/include/sml/sml_shared.h index 1a4f1d5..6b7478e 100644 --- a/sml/include/sml/sml_shared.h +++ b/sml/include/sml/sml_shared.h @@ -109,6 +109,16 @@ int sml_buf_optional_is_skipped(sml_buffer *buf); // Prints arbitrarily byte string to stdout with printf void hexdump(unsigned char *buffer, size_t buffer_len); +// Allow user to intercept error messages +extern void (*sml_error)(const char *format, ...) + // http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + __attribute__((format(printf, 1, 2))); + +// default sml_error function, prints message to stderr as before +void sml_error_default(const char *format, ...) + // http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + __attribute__((format(printf, 1, 2))); + #ifdef __cplusplus } #endif diff --git a/sml/src/sml_file.c b/sml/src/sml_file.c index dd7a78a..724232a 100644 --- a/sml/src/sml_file.c +++ b/sml/src/sml_file.c @@ -49,7 +49,7 @@ sml_file *sml_file_parse(unsigned char *buffer, size_t buffer_len) { msg = sml_message_parse(buf); if (sml_buf_has_errors(buf)) { - fprintf(stderr, "libsml: warning: could not read the whole file\n"); + sml_error("warning: could not read the whole file"); break; } diff --git a/sml/src/sml_message.c b/sml/src/sml_message.c index 589b578..a4d83f4 100644 --- a/sml/src/sml_message.c +++ b/sml/src/sml_message.c @@ -209,7 +209,7 @@ sml_message_body *sml_message_body_parse(sml_buffer *buf) { msg_body->data = sml_attention_response_parse(buf); break; default: - fprintf(stderr, "libsml: error: message type %04X not yet implemented\n", *(msg_body->tag)); + sml_error("error: message type %04X not yet implemented", *(msg_body->tag)); break; } @@ -279,8 +279,7 @@ void sml_message_body_write(sml_message_body *message_body, sml_buffer *buf) { sml_attention_response_write((sml_attention_response *)message_body->data, buf); break; default: - fprintf(stderr, "libsml: error: message type %04X not yet implemented\n", - *(message_body->tag)); + sml_error("error: message type %04X not yet implemented", *(message_body->tag)); break; } } @@ -334,8 +333,7 @@ void sml_message_body_free(sml_message_body *message_body) { sml_attention_response_free((sml_attention_response *)message_body->data); break; default: - fprintf(stderr, "libsml: NYI: %s for message type %04X\n", __func__, - *(message_body->tag)); + sml_error("NYI: %s for message type %04X", __func__, *(message_body->tag)); break; } sml_number_free(message_body->tag); diff --git a/sml/src/sml_shared.c b/sml/src/sml_shared.c index 32dca56..95727f0 100644 --- a/sml/src/sml_shared.c +++ b/sml/src/sml_shared.c @@ -17,9 +17,29 @@ // along with libSML. If not, see . #include +#include #include #include +void (*sml_error)(const char *format, ...); + +void sml_error_default(const char *format, ...) { + va_list args; + va_start(args, format); + + char *format2 = malloc(9 + strlen(format) + 1 + 1); + strcpy(format2, "libsml: "); + strcat(format2, format); + strcat(format2, "\n"); + + vfprintf(stderr, format2, args); + + free(format2); +} + +// http://www.faqs.org/docs/Linux-HOWTO/Program-Library-HOWTO.html#INIT-AND-CLEANUP +void __attribute__((constructor)) sml_init() { sml_error = *sml_error_default; } + int sml_buf_get_next_length(sml_buffer *buf) { int length = 0; diff --git a/sml/src/sml_transport.c b/sml/src/sml_transport.c index 17f868c..c1c2e81 100644 --- a/sml/src/sml_transport.c +++ b/sml/src/sml_transport.c @@ -47,7 +47,7 @@ size_t sml_read(int fd, fd_set *set, unsigned char *buffer, size_t len) { if (r < 0) { if (errno == EINTR || errno == EAGAIN) continue; // should be ignored - fprintf(stderr, "libsml: sml_read(): read error\n"); + sml_error("sml_read(): read error"); return 0; } tr += r; @@ -68,7 +68,7 @@ size_t sml_transport_read(int fd, unsigned char *buffer, size_t max_len) { if (max_len < 8) { // prevent buffer overflow - fprintf(stderr, "libsml: error: sml_transport_read(): passed buffer too small!\n"); + sml_error("error: sml_transport_read(): passed buffer too small!"); return 0; } @@ -104,7 +104,7 @@ size_t sml_transport_read(int fd, unsigned char *buffer, size_t max_len) { return len; } else { // don't read other escaped sequences yet - fprintf(stderr, "libsml: error: unrecognized sequence\n"); + sml_error("error: unrecognized sequence"); return 0; } } diff --git a/sml/src/sml_tree.c b/sml/src/sml_tree.c index e54561e..796a0f3 100644 --- a/sml/src/sml_tree.c +++ b/sml/src/sml_tree.c @@ -282,7 +282,7 @@ void sml_proc_par_value_write(sml_proc_par_value *value, sml_buffer *buf) { sml_time_write(value->data.time, buf); break; default: - fprintf(stderr, "libsml: error: unknown tag in %s\n", __func__); + sml_error("error: unknown tag in %s", __func__); } } diff --git a/sml/src/sml_value.c b/sml/src/sml_value.c index fab9d0e..b945e2b 100644 --- a/sml/src/sml_value.c +++ b/sml/src/sml_value.c @@ -134,7 +134,7 @@ double sml_value_to_double(sml_value *value) { break; default: - fprintf(stderr, "libsml: error: unknown type %d in %s\n", value->type, __func__); + sml_error("error: unknown type %d in %s", value->type, __func__); return 0; } } diff --git a/test/Makefile b/test/Makefile index 67cbeb7..3456ab7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -24,7 +24,8 @@ OBJS = \ src/sml_file_test.o \ src/sml_open_request_test.o \ src/sml_get_profile_pack_request_test.o \ - src/sml_message_test.o + src/sml_message_test.o \ + src/sml_error_test.o test_run: libsml test @./test diff --git a/test/src/sml_error_test.c b/test/src/sml_error_test.c new file mode 100644 index 0000000..4ce89e1 --- /dev/null +++ b/test/src/sml_error_test.c @@ -0,0 +1,78 @@ +// Copyright 2020 volkszaehler.org +// +// This file is part of libSML. +// +// libSML is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// libSML is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with libSML. If not, see . + +#include "../unity/unity_fixture.h" +#include "test_helper.h" +#include + +#include + +TEST_GROUP(sml_error); + +TEST_SETUP(sml_error) { } + +TEST_TEAR_DOWN(sml_error) { } + +TEST(sml_error, default) { + char *tmpfile="./test_sml_error.tmp"; // FIXME: should not be hardcoded + char *test_string="this is a test"; + char *expected_output="libsml: this is a test\n"; + + FILE *capture = fopen(tmpfile, "w"); + TEST_ASSERT_NOT_NULL(capture); + int stderr_backup=dup(2); // duplicate old stderr so it won't be closed + dup2(fileno(capture),2); // assign capture-file to stderr + + sml_error(test_string); + + fclose(capture); + dup2(stderr_backup,2); // restore stderr + close(stderr_backup); // discard backup + + size_t len=strlen(expected_output)+1; + char *buf=malloc(len); + + capture = fopen(tmpfile, "r"); + size_t got=fread(buf,1,len-1,capture); + *(buf+got)=0; // add zero termination + //fprintf(stderr,"got: `%s`\n\n",buf); + fclose(capture); + unlink(tmpfile); + + TEST_ASSERT_EQUAL(0, strcmp(expected_output,buf)); + + free(buf); +} + +const char *msg=NULL; +void my_sml_error(const char *format, ... ){ + msg=format; +} + +TEST(sml_error, custom) { + char *test_string="this is a test"; + sml_error = my_sml_error; + sml_error(test_string,1,2,3); + sml_error = sml_error_default; + TEST_ASSERT_NOT_NULL(msg); + TEST_ASSERT_EQUAL(0, strcmp(msg,test_string)); +} + +TEST_GROUP_RUNNER(sml_error) { + RUN_TEST_CASE(sml_error, default); + RUN_TEST_CASE(sml_error, custom); +} diff --git a/test/test_main.c b/test/test_main.c index 4f42654..57f6008 100644 --- a/test/test_main.c +++ b/test/test_main.c @@ -35,6 +35,7 @@ static void runAllTests() { RUN_TEST_GROUP(sml_get_profile_pack_request); RUN_TEST_GROUP(sml_message); RUN_TEST_GROUP(sml_file); + RUN_TEST_GROUP(sml_error); } int main(int argc, char * argv[]) {