Skip to content

Commit fc74436

Browse files
committed
Error percolation with nice error messages in debug mode
1 parent e0855b2 commit fc74436

6 files changed

Lines changed: 166 additions & 31 deletions

File tree

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
*.exe
12
.idea
23
*build*
34
*_export.h
4-
5-
test_amalg.c
5+
6+
test_amalg.c
67
cmake/scripts/

CMakeLists.txt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,18 @@ add_subdirectory("${PROJECT_NAME}")
5757

5858
include(CTest)
5959
option(BUILD_TESTING "Build the tests" ON)
60-
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
61-
option(C89STRINGUTILS_BUILD_TESTING "Build the tests" ON)
62-
else()
63-
option(C89STRINGUTILS_BUILD_TESTING "Build the tests" OFF)
60+
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
61+
option(C89STRINGUTILS_BUILD_TESTING "Build the tests" ON)
62+
else()
63+
option(C89STRINGUTILS_BUILD_TESTING "Build the tests" OFF)
6464
endif()
6565

66+
set(CDD_CHARSET "Unicode" CACHE STRING "Charset to use: Unicode or ANSI")
67+
set(CDD_THREADING "Multi-threaded" CACHE STRING "Threading: Multi-threaded or Single-threaded")
68+
set(CDD_MSVC_RTC "" CACHE STRING "MSVC Runtime Checks: RTC1, RTCs, RTCu, or empty")
69+
set(CDD_LTO OFF CACHE BOOL "Enable Link-Time Optimization (LTO)")
70+
set(CDD_DEPS "System" CACHE STRING "Dependency resolution strategy")
71+
6672
if (CDD_CHARSET STREQUAL "Unicode")
6773
add_compile_definitions(UNICODE _UNICODE)
6874
endif()

c89stringutils/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
get_filename_component(LIBRARY_NAME "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
22

3-
set(Header_Files "c89stringutils_string_extras.h")
3+
set(Header_Files "c89stringutils_string_extras.h" "c89stringutils_log.h")
44
source_group("Header Files" FILES "${Header_Files}")
55

66
set(Source_Files "c89stringutils_string_extras.c")
@@ -61,19 +61,23 @@ if (C89STRINGUTILS_BUILD_AMALGAMATION)
6161
set(amalg_file "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_amalgamation.h")
6262
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
6363
"c89stringutils_export_pregen.h"
64+
"c89stringutils_log.h"
6465
"c89stringutils_string_extras.h"
6566
"c89stringutils_string_extras.c")
6667

6768
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/c89stringutils_export_pregen.h" amalg_export)
69+
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/c89stringutils_log.h" amalg_log)
6870
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/c89stringutils_string_extras.h" amalg_header)
6971
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/c89stringutils_string_extras.c" amalg_source)
7072

7173
string(REPLACE "#include \"c89stringutils_export.h\"" "" amalg_header "${amalg_header}")
7274
string(REPLACE "#include \"c89stringutils_string_extras.h\"" "" amalg_source "${amalg_source}")
75+
string(REPLACE "#include \"c89stringutils_log.h\"" "" amalg_source "${amalg_source}")
7376

7477
set(amalgamation "/* c89stringutils Amalgamation STB-style */\n\n")
7578
string(APPEND amalgamation "#ifndef C89STRINGUTILS_AMALGAMATION_H\n#define C89STRINGUTILS_AMALGAMATION_H\n\n")
7679
string(APPEND amalgamation "${amalg_export}\n\n")
80+
string(APPEND amalgamation "${amalg_log}\n\n")
7781
string(APPEND amalgamation "${amalg_header}\n\n")
7882
string(APPEND amalgamation "#ifdef C89STRINGUTILS_IMPLEMENTATION\n\n")
7983
string(APPEND amalgamation "${amalg_source}\n\n")
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef C89STRINGUTILS_LOG_H
2+
#define C89STRINGUTILS_LOG_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif /* __cplusplus */
7+
8+
/* clang-format off */
9+
#include <stdio.h>
10+
/* clang-format on */
11+
12+
#ifndef LOG_DEBUG
13+
#ifdef DEBUG
14+
#if defined(_MSC_VER) && _MSC_VER < 1400
15+
/* MSVC older than 2005 doesn't support variadic macros well */
16+
#define LOG_DEBUG(fmt, ...)
17+
#else
18+
#if defined(__GNUC__) || defined(__clang__)
19+
#pragma GCC diagnostic push
20+
#pragma GCC diagnostic ignored "-Wvariadic-macros"
21+
#endif /* defined(__GNUC__) || defined(__clang__) */
22+
#define LOG_DEBUG(fmt, ...) fprintf(stderr, "[DEBUG] " fmt, ##__VA_ARGS__)
23+
#if defined(__GNUC__) || defined(__clang__)
24+
#pragma GCC diagnostic pop
25+
#endif /* defined(__GNUC__) || defined(__clang__) */
26+
#endif
27+
#else
28+
#if defined(__GNUC__) || defined(__clang__)
29+
#pragma GCC diagnostic push
30+
#pragma GCC diagnostic ignored "-Wvariadic-macros"
31+
#endif /* defined(__GNUC__) || defined(__clang__) */
32+
#define LOG_DEBUG(fmt, ...)
33+
#if defined(__GNUC__) || defined(__clang__)
34+
#pragma GCC diagnostic pop
35+
#endif /* defined(__GNUC__) || defined(__clang__) */
36+
#endif /* DEBUG */
37+
#endif /* !LOG_DEBUG */
38+
39+
#ifdef __cplusplus
40+
}
41+
#endif /* __cplusplus */
42+
43+
#endif /* !C89STRINGUTILS_LOG_H */

c89stringutils/c89stringutils_string_extras.c

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
/* clang-format off */
77
#include "c89stringutils_string_extras.h"
8+
#include "c89stringutils_log.h"
89

910
#include <stdarg.h>
1011
#include <stdio.h>
@@ -31,35 +32,35 @@
3132
#endif /* ANY_BSD */
3233

3334
static int wtf_snprintf(char *buffer, size_t count, const char *format, ...) {
34-
int result;
35+
int rc;
3536
va_list args;
3637
va_start(args, format);
3738
#if defined(_MSC_VER)
38-
result = _vsnprintf_s(buffer, count, _TRUNCATE, format, args);
39+
rc = _vsnprintf_s(buffer, count, _TRUNCATE, format, args);
3940
#else
40-
result = _vsnprintf(buffer, count, format, args);
41+
rc = _vsnprintf(buffer, count, format, args);
4142
#endif
4243
va_end(args);
4344
/* In the case where the string entirely filled the buffer, _vsnprintf will
4445
not null-terminate it, but snprintf must. */
4546
if (count > 0)
4647
buffer[count - 1] = '\0';
47-
return result;
48+
return rc;
4849
}
4950

5051
static int wtf_vsnprintf(char *buffer, size_t count, const char *format,
5152
va_list args) {
52-
int result;
53+
int rc;
5354
#if defined(_MSC_VER)
54-
result = _vsnprintf_s(buffer, count, _TRUNCATE, format, args);
55+
rc = _vsnprintf_s(buffer, count, _TRUNCATE, format, args);
5556
#else
56-
result = _vsnprintf(buffer, count, format, args);
57+
rc = _vsnprintf(buffer, count, format, args);
5758
#endif
5859
/* In the case where the string entirely filled the buffer, _vsnprintf will
5960
not null-terminate it, but vsnprintf must. */
6061
if (count > 0)
6162
buffer[count - 1] = '\0';
62-
return result;
63+
return rc;
6364
}
6465

6566
#define vsnprintf(buffer, count, format, args) \
@@ -72,8 +73,11 @@ static int wtf_vsnprintf(char *buffer, size_t count, const char *format,
7273

7374
#define HAVE_STRNCASECMP_H
7475

75-
#define strncasecmp _strnicmp
76-
#define strcasecmp _stricmp
76+
int strncasecmp(const char *s1, const char *s2, size_t n) {
77+
return _strnicmp(s1, s2, n);
78+
}
79+
80+
int strcasecmp(const char *s1, const char *s2) { return _stricmp(s1, s2); }
7781

7882
#endif /* !HAVE_STRNCASECMP_H */
7983

@@ -199,55 +203,79 @@ size_t strerrorlen_s(errno_t errnum) {
199203
#define INIT_SZ 128
200204

201205
extern int vasprintf(char **str, const char *fmt, va_list ap) {
202-
int ret;
206+
int rc;
203207
va_list ap2;
204208
char *string, *newstr;
205209
size_t len;
206210

207-
if ((string = (char *)malloc(INIT_SZ)) == NULL)
211+
if ((string = (char *)malloc(INIT_SZ)) == NULL) {
212+
rc = -1;
208213
goto fail;
214+
}
209215

210216
VA_COPY(ap2, ap);
211-
ret = vsnprintf(string, INIT_SZ, fmt, ap2);
217+
rc = vsnprintf(string, INIT_SZ, fmt, ap2);
212218
va_end(ap2);
213-
if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */
219+
if (rc >= 0 && rc < INIT_SZ) { /* succeeded with initial alloc */
214220
*str = string;
215-
} else if (ret == INT_MAX || ret < 0) { /* Bad length */
221+
} else if (rc == INT_MAX || rc < 0) { /* Bad length */
216222
free(string);
223+
rc = -1;
217224
goto fail;
218225
} else { /* bigger than initial, realloc allowing for nul */
219-
len = (size_t)ret + 1;
226+
len = (size_t)rc + 1;
220227
if ((newstr = (char *)realloc(string, len)) == NULL) {
221228
free(string);
229+
rc = -1;
222230
goto fail;
223231
}
224232
VA_COPY(ap2, ap);
225-
ret = vsnprintf(newstr, len, fmt, ap2);
233+
rc = vsnprintf(newstr, len, fmt, ap2);
226234
va_end(ap2);
227-
if (ret < 0 || (size_t)ret >= len) { /* failed with realloc'ed string */
235+
if (rc < 0 || (size_t)rc >= len) { /* failed with realloc'ed string */
228236
free(newstr);
237+
rc = -1;
229238
goto fail;
230239
}
231240
*str = newstr;
232241
}
233-
return ret;
242+
return rc;
234243

235244
fail:
245+
if (rc != 0) {
246+
#ifdef _MSC_VER
247+
char errbuf[256];
248+
strerror_s(errbuf, sizeof(errbuf), errno);
249+
LOG_DEBUG("vasprintf failed with rc=%d, error=%s\n", rc, errbuf);
250+
#else
251+
LOG_DEBUG("vasprintf failed with rc=%d, error=%s\n", rc, strerror(errno));
252+
#endif
253+
}
236254
*str = NULL;
237255
errno = ENOMEM;
238-
return -1;
256+
return rc;
239257
}
240258

241259
extern int asprintf(char **str, const char *fmt, ...) {
242260
va_list ap;
243-
int ret;
261+
int rc;
244262

245263
*str = NULL;
246264
va_start(ap, fmt);
247-
ret = vasprintf(str, fmt, ap);
265+
rc = vasprintf(str, fmt, ap);
248266
va_end(ap);
249267

250-
return ret;
268+
if (rc < 0) {
269+
#ifdef _MSC_VER
270+
char errbuf[256];
271+
strerror_s(errbuf, sizeof(errbuf), errno);
272+
LOG_DEBUG("asprintf failed with rc=%d, error=%s\n", rc, errbuf);
273+
#else
274+
LOG_DEBUG("asprintf failed with rc=%d, error=%s\n", rc, strerror(errno));
275+
#endif
276+
}
277+
278+
return rc;
251279
}
252280

253281
#endif /* !HAVE_ASPRINTF */

c89stringutils/tests/test_string_extras.h

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
#include <c89stringutils_string_extras.h>
1212
#include <stdlib.h>
13+
#include <errno.h>
1314
/* clang-format on */
1415

1516
static const char *buffer = "hello world";
@@ -27,7 +28,8 @@ TEST x_strnstr_should_fail(void) {
2728

2829
TEST x_asprintf_should_succeed(void) {
2930
char *s = NULL;
30-
asprintf(&s, "foo%s", "bar");
31+
int rc = asprintf(&s, "foo%s", "bar");
32+
ASSERT_EQ(6, rc);
3133
ASSERT_STR_EQ("foobar", s);
3234
free(s);
3335
PASS();
@@ -42,12 +44,63 @@ TEST x_jasprintf_should_succeed(void) {
4244
PASS();
4345
}
4446

47+
TEST x_strcasecmp_should_succeed(void) {
48+
ASSERT_EQ(0, strcasecmp("HeLlO", "hElLo"));
49+
ASSERT(strcasecmp("apple", "banana") < 0);
50+
ASSERT(strcasecmp("banana", "apple") > 0);
51+
PASS();
52+
}
53+
54+
TEST x_strncasecmp_should_succeed(void) {
55+
ASSERT_EQ(0, strncasecmp("HeLlO World", "hElLo Earth", 5));
56+
ASSERT(strncasecmp("apple pie", "apple tart", 9) < 0);
57+
PASS();
58+
}
59+
60+
TEST x_strcasestr_should_succeed(void) {
61+
const char *haystack = "The Quick Brown Fox";
62+
ASSERT_STR_EQ("Brown Fox", strcasestr(haystack, "bRoWn"));
63+
ASSERT_EQ((char *)NULL, strcasestr(haystack, "red"));
64+
PASS();
65+
}
66+
67+
TEST x_strerrorlen_s_should_succeed(void) {
68+
ASSERT(strerrorlen_s(ENOMEM) > 0);
69+
ASSERT(strerrorlen_s(400) == 8); /* ESNULLP */
70+
PASS();
71+
}
72+
73+
/* vasprintf can be tested implicitly via asprintf, but let's test it directly
74+
* just in case */
75+
static int test_vasprintf_wrapper(char **str, const char *fmt, ...) {
76+
int rc;
77+
va_list ap;
78+
va_start(ap, fmt);
79+
rc = vasprintf(str, fmt, ap);
80+
va_end(ap);
81+
return rc;
82+
}
83+
84+
TEST x_vasprintf_should_succeed(void) {
85+
char *s = NULL;
86+
int rc = test_vasprintf_wrapper(&s, "test %d", 123);
87+
ASSERT_EQ(8, rc);
88+
ASSERT_STR_EQ("test 123", s);
89+
free(s);
90+
PASS();
91+
}
92+
4593
/* Suites can group multiple tests with common setup. */
4694
SUITE(strnstr_suite) {
4795
RUN_TEST(x_strnstr_should_succeed);
4896
RUN_TEST(x_strnstr_should_fail);
4997
RUN_TEST(x_asprintf_should_succeed);
5098
RUN_TEST(x_jasprintf_should_succeed);
99+
RUN_TEST(x_strcasecmp_should_succeed);
100+
RUN_TEST(x_strncasecmp_should_succeed);
101+
RUN_TEST(x_strcasestr_should_succeed);
102+
RUN_TEST(x_strerrorlen_s_should_succeed);
103+
RUN_TEST(x_vasprintf_should_succeed);
51104
}
52105

53106
#ifdef __cplusplus

0 commit comments

Comments
 (0)