diff options
-rw-r--r-- | ccan/str/_info | 13 | ||||
-rw-r--r-- | ccan/str/debug.c | 102 | ||||
-rw-r--r-- | ccan/str/str.h | 63 | ||||
-rw-r--r-- | ccan/str/str_debug.h | 27 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isalnum.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isalpha.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isascii.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isblank.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-iscntrl.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isdigit.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-islower.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isprint.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-ispunct.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isspace.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isupper.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-isxdigit.c | 23 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-strchr.c | 18 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-strrchr.c | 18 | ||||
-rw-r--r-- | ccan/str/test/compile_fail-strstr.c | 18 | ||||
-rw-r--r-- | ccan/str/test/debug.c | 5 |
20 files changed, 540 insertions, 0 deletions
diff --git a/ccan/str/_info b/ccan/str/_info index 0438ccfd..ea314dbf 100644 --- a/ccan/str/_info +++ b/ccan/str/_info @@ -8,6 +8,18 @@ * This is a grab bag of functions for string operations, designed to enhance * the standard string.h. * + * Note that if you define CCAN_STR_DEBUG, you will get extra compile + * checks on common misuses of the following functions (they will now + * be out-of-line, so there is a runtime penalty!). + * + * strstr, strchr, strrchr: + * Return const char * if first argument is const (gcc only). + * + * isalnum, isalpha, isascii, isblank, iscntrl, isdigit, isgraph, + * islower, isprint, ispunct, isspace, isupper, isxdigit: + * Static and runtime check that input is EOF or an *unsigned* + * char, as per C standard (really!). + * * Example: * #include <stdio.h> * #include <ccan/str/str.h> @@ -32,6 +44,7 @@ int main(int argc, char *argv[]) return 1; if (strcmp(argv[1], "depends") == 0) { + printf("ccan/build_assert\n"); return 0; } diff --git a/ccan/str/debug.c b/ccan/str/debug.c new file mode 100644 index 00000000..004a874f --- /dev/null +++ b/ccan/str/debug.c @@ -0,0 +1,102 @@ +#include "config.h" +#include <ccan/str/str_debug.h> +#include <assert.h> +#include <ctype.h> +#include <string.h> + +#ifdef CCAN_STR_DEBUG +/* Because we mug the real ones with macros, we need our own wrappers. */ +int str_isalnum(int i) +{ + assert(i >= -1 && i < 256); + return isalnum(i); +} + +int str_isalpha(int i) +{ + assert(i >= -1 && i < 256); + return isalpha(i); +} + +int str_isascii(int i) +{ + assert(i >= -1 && i < 256); + return isascii(i); +} + +int str_isblank(int i) +{ + assert(i >= -1 && i < 256); + return isblank(i); +} + +int str_iscntrl(int i) +{ + assert(i >= -1 && i < 256); + return iscntrl(i); +} + +int str_isdigit(int i) +{ + assert(i >= -1 && i < 256); + return isdigit(i); +} + +int str_isgraph(int i) +{ + assert(i >= -1 && i < 256); + return isgraph(i); +} + +int str_islower(int i) +{ + assert(i >= -1 && i < 256); + return islower(i); +} + +int str_isprint(int i) +{ + assert(i >= -1 && i < 256); + return isprint(i); +} + +int str_ispunct(int i) +{ + assert(i >= -1 && i < 256); + return ispunct(i); +} + +int str_isspace(int i) +{ + assert(i >= -1 && i < 256); + return isspace(i); +} + +int str_isupper(int i) +{ + assert(i >= -1 && i < 256); + return isupper(i); +} + +int str_isxdigit(int i) +{ + assert(i >= -1 && i < 256); + return isxdigit(i); +} + + +char *str_strstr(const char *haystack, const char *needle) +{ + return strstr(haystack, needle); +} + +char *str_strchr(const char *haystack, int c) +{ + return strchr(haystack, c); +} + +char *str_strrchr(const char *haystack, int c) +{ + return strrchr(haystack, c); +} +#endif diff --git a/ccan/str/str.h b/ccan/str/str.h index bf858f13..54afca0f 100644 --- a/ccan/str/str.h +++ b/ccan/str/str.h @@ -1,7 +1,9 @@ #ifndef CCAN_STR_H #define CCAN_STR_H +#include "config.h" #include <string.h> #include <stdbool.h> +#include <ctype.h> /** * streq - Are two strings equal? @@ -69,4 +71,65 @@ static inline bool strends(const char *str, const char *postfix) */ size_t strcount(const char *haystack, const char *needle); +#include <ccan/str/str_debug.h> + +/* These checks force things out of line, hence they are under DEBUG. */ +#ifdef CCAN_STR_DEBUG +#include <ccan/build_assert/build_assert.h> + +/* These are commonly misused: they take -1 or an *unsigned* char value. */ +#undef isalnum +#undef isalpha +#undef isascii +#undef isblank +#undef iscntrl +#undef isdigit +#undef isgraph +#undef islower +#undef isprint +#undef ispunct +#undef isspace +#undef isupper +#undef isxdigit + +/* You can use a char if char is unsigned. */ +#if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF +#define str_check_arg_(i) \ + ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \ + char) \ + || (char)255 > 0)) +#else +#define str_check_arg_(i) (i) +#endif + +#define isalnum(i) str_isalnum(str_check_arg_(i)) +#define isalpha(i) str_isalpha(str_check_arg_(i)) +#define isascii(i) str_isascii(str_check_arg_(i)) +#define isblank(i) str_isblank(str_check_arg_(i)) +#define iscntrl(i) str_iscntrl(str_check_arg_(i)) +#define isdigit(i) str_isdigit(str_check_arg_(i)) +#define isgraph(i) str_isgraph(str_check_arg_(i)) +#define islower(i) str_islower(str_check_arg_(i)) +#define isprint(i) str_isprint(str_check_arg_(i)) +#define ispunct(i) str_ispunct(str_check_arg_(i)) +#define isspace(i) str_isspace(str_check_arg_(i)) +#define isupper(i) str_isupper(str_check_arg_(i)) +#define isxdigit(i) str_isxdigit(str_check_arg_(i)) + +#if HAVE_TYPEOF +/* With GNU magic, we can make const-respecting standard string functions. */ +#undef strstr +#undef strchr +#undef strrchr + +/* + 0 is needed to decay array into pointer. */ +#define strstr(haystack, needle) \ + ((typeof((haystack) + 0))str_strstr((haystack), (needle))) +#define strchr(haystack, c) \ + ((typeof((haystack) + 0))str_strchr((haystack), (c))) +#define strrchr(haystack, c) \ + ((typeof((haystack) + 0))str_strrchr((haystack), (c))) +#endif +#endif /* CCAN_STR_DEBUG */ + #endif /* CCAN_STR_H */ diff --git a/ccan/str/str_debug.h b/ccan/str/str_debug.h new file mode 100644 index 00000000..4545ccaa --- /dev/null +++ b/ccan/str/str_debug.h @@ -0,0 +1,27 @@ +#ifndef CCAN_STR_DEBUG_H +#define CCAN_STR_DEBUG_H + +/* #define CCAN_STR_DEBUG 1 */ + +#ifdef CCAN_STR_DEBUG +/* Because we mug the real ones with macros, we need our own wrappers. */ +int str_isalnum(int i); +int str_isalpha(int i); +int str_isascii(int i); +int str_isblank(int i); +int str_iscntrl(int i); +int str_isdigit(int i); +int str_isgraph(int i); +int str_islower(int i); +int str_isprint(int i); +int str_ispunct(int i); +int str_isspace(int i); +int str_isupper(int i); +int str_isxdigit(int i); + +char *str_strstr(const char *haystack, const char *needle); +char *str_strchr(const char *s, int c); +char *str_strrchr(const char *s, int c); +#endif /* CCAN_STR_DEBUG */ + +#endif /* CCAN_STR_DEBUG_H */ diff --git a/ccan/str/test/compile_fail-isalnum.c b/ccan/str/test/compile_fail-isalnum.c new file mode 100644 index 00000000..1b6a3633 --- /dev/null +++ b/ccan/str/test/compile_fail-isalnum.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isalnum. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isalnum(c); +} diff --git a/ccan/str/test/compile_fail-isalpha.c b/ccan/str/test/compile_fail-isalpha.c new file mode 100644 index 00000000..76de1504 --- /dev/null +++ b/ccan/str/test/compile_fail-isalpha.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isalpha. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isalpha(c); +} diff --git a/ccan/str/test/compile_fail-isascii.c b/ccan/str/test/compile_fail-isascii.c new file mode 100644 index 00000000..af6e64d5 --- /dev/null +++ b/ccan/str/test/compile_fail-isascii.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isascii. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isascii(c); +} diff --git a/ccan/str/test/compile_fail-isblank.c b/ccan/str/test/compile_fail-isblank.c new file mode 100644 index 00000000..86a33503 --- /dev/null +++ b/ccan/str/test/compile_fail-isblank.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isblank. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isblank(c); +} diff --git a/ccan/str/test/compile_fail-iscntrl.c b/ccan/str/test/compile_fail-iscntrl.c new file mode 100644 index 00000000..5ae783b1 --- /dev/null +++ b/ccan/str/test/compile_fail-iscntrl.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check iscntrl. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return iscntrl(c); +} diff --git a/ccan/str/test/compile_fail-isdigit.c b/ccan/str/test/compile_fail-isdigit.c new file mode 100644 index 00000000..68d81ba4 --- /dev/null +++ b/ccan/str/test/compile_fail-isdigit.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isdigit. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isdigit(c); +} diff --git a/ccan/str/test/compile_fail-islower.c b/ccan/str/test/compile_fail-islower.c new file mode 100644 index 00000000..9dec95f6 --- /dev/null +++ b/ccan/str/test/compile_fail-islower.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check islower. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return islower(c); +} diff --git a/ccan/str/test/compile_fail-isprint.c b/ccan/str/test/compile_fail-isprint.c new file mode 100644 index 00000000..30f7639c --- /dev/null +++ b/ccan/str/test/compile_fail-isprint.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isprint. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isprint(c); +} diff --git a/ccan/str/test/compile_fail-ispunct.c b/ccan/str/test/compile_fail-ispunct.c new file mode 100644 index 00000000..f1ae13ac --- /dev/null +++ b/ccan/str/test/compile_fail-ispunct.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check ispunct. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return ispunct(c); +} diff --git a/ccan/str/test/compile_fail-isspace.c b/ccan/str/test/compile_fail-isspace.c new file mode 100644 index 00000000..c94c49e4 --- /dev/null +++ b/ccan/str/test/compile_fail-isspace.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isspace. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isspace(c); +} diff --git a/ccan/str/test/compile_fail-isupper.c b/ccan/str/test/compile_fail-isupper.c new file mode 100644 index 00000000..f23dd659 --- /dev/null +++ b/ccan/str/test/compile_fail-isupper.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isupper. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isupper(c); +} diff --git a/ccan/str/test/compile_fail-isxdigit.c b/ccan/str/test/compile_fail-isxdigit.c new file mode 100644 index 00000000..6a7377fd --- /dev/null +++ b/ccan/str/test/compile_fail-isxdigit.c @@ -0,0 +1,23 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF +#error We need typeof to check isxdigit. +#endif + char +#else + unsigned char +#endif + c = argv[0][0]; + +#ifdef FAIL + /* Fake fail on unsigned char platforms. */ + c = 255; + BUILD_ASSERT(c < 0); +#endif + + return isxdigit(c); +} diff --git a/ccan/str/test/compile_fail-strchr.c b/ccan/str/test/compile_fail-strchr.c new file mode 100644 index 00000000..74a7314d --- /dev/null +++ b/ccan/str/test/compile_fail-strchr.c @@ -0,0 +1,18 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_TYPEOF + #error We need typeof to check strstr. +#endif +#else + const +#endif + char *ret; + const char *str = "hello"; + + ret = strchr(str, 'l'); + return ret ? 0 : 1; +} diff --git a/ccan/str/test/compile_fail-strrchr.c b/ccan/str/test/compile_fail-strrchr.c new file mode 100644 index 00000000..ba7d17e0 --- /dev/null +++ b/ccan/str/test/compile_fail-strrchr.c @@ -0,0 +1,18 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_TYPEOF + #error We need typeof to check strstr. +#endif +#else + const +#endif + char *ret; + const char *str = "hello"; + + ret = strrchr(str, 'l'); + return ret ? 0 : 1; +} diff --git a/ccan/str/test/compile_fail-strstr.c b/ccan/str/test/compile_fail-strstr.c new file mode 100644 index 00000000..deefef65 --- /dev/null +++ b/ccan/str/test/compile_fail-strstr.c @@ -0,0 +1,18 @@ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/str.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if !HAVE_TYPEOF + #error We need typeof to check strstr. +#endif +#else + const +#endif + char *ret; + const char *str = "hello"; + + ret = strstr(str, "hell"); + return ret ? 0 : 1; +} diff --git a/ccan/str/test/debug.c b/ccan/str/test/debug.c new file mode 100644 index 00000000..4bd384f2 --- /dev/null +++ b/ccan/str/test/debug.c @@ -0,0 +1,5 @@ +/* We can't use the normal "#include the .c file" trick, since this is + contaminated by str.h's macro overrides. So we put it in all tests + like this. */ +#define CCAN_STR_DEBUG 1 +#include <ccan/str/debug.c> |