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[]) {