summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ccan/str/_info13
-rw-r--r--ccan/str/debug.c102
-rw-r--r--ccan/str/str.h63
-rw-r--r--ccan/str/str_debug.h27
-rw-r--r--ccan/str/test/compile_fail-isalnum.c23
-rw-r--r--ccan/str/test/compile_fail-isalpha.c23
-rw-r--r--ccan/str/test/compile_fail-isascii.c23
-rw-r--r--ccan/str/test/compile_fail-isblank.c23
-rw-r--r--ccan/str/test/compile_fail-iscntrl.c23
-rw-r--r--ccan/str/test/compile_fail-isdigit.c23
-rw-r--r--ccan/str/test/compile_fail-islower.c23
-rw-r--r--ccan/str/test/compile_fail-isprint.c23
-rw-r--r--ccan/str/test/compile_fail-ispunct.c23
-rw-r--r--ccan/str/test/compile_fail-isspace.c23
-rw-r--r--ccan/str/test/compile_fail-isupper.c23
-rw-r--r--ccan/str/test/compile_fail-isxdigit.c23
-rw-r--r--ccan/str/test/compile_fail-strchr.c18
-rw-r--r--ccan/str/test/compile_fail-strrchr.c18
-rw-r--r--ccan/str/test/compile_fail-strstr.c18
-rw-r--r--ccan/str/test/debug.c5
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>