[ccan] [PATCH] argcheck: a module to check argument ranges

Peter Hutterer peter.hutterer at who-t.net
Tue Nov 19 11:30:06 EST 2013


This code provides some macros to check arguments for valid value ranges.
Consider this a mild version of assert(3), because all it does is to log
a message and continue.

These macros don't replace error handling, but they are useful in
situations where an error is unexpected but not common, i.e.
this shouldn't happen but if it does let me know".

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
I found these quite useful, especially when prototyping something where it's
easier to throw in a few debug messages than provide full-scale error
handling code. Plus, there's plenty of code where error handling isn't
common but you'd still want to know if something is outside the expected
range (see the state machine example in _info).

 Makefile-ccan            |   1 +
 ccan/argcheck/LICENSE    |   1 +
 ccan/argcheck/_info      |  71 +++++++++++++++++
 ccan/argcheck/argcheck.h | 198 +++++++++++++++++++++++++++++++++++++++++++++++
 ccan/argcheck/test/run.c | 102 ++++++++++++++++++++++++
 5 files changed, 373 insertions(+)
 create mode 120000 ccan/argcheck/LICENSE
 create mode 100644 ccan/argcheck/_info
 create mode 100644 ccan/argcheck/argcheck.h
 create mode 100644 ccan/argcheck/test/run.c

diff --git a/Makefile-ccan b/Makefile-ccan
index 7781657..cbf1c2f 100644
--- a/Makefile-ccan
+++ b/Makefile-ccan
@@ -8,6 +8,7 @@ CFLAGS = $(CCAN_CFLAGS) -I. $(DEPGEN)
 
 # Modules which are just a header:
 MODS_NO_SRC := alignof \
+	argcheck \
 	array_size \
 	asearch \
 	bitmap \
diff --git a/ccan/argcheck/LICENSE b/ccan/argcheck/LICENSE
new file mode 120000
index 0000000..2354d12
--- /dev/null
+++ b/ccan/argcheck/LICENSE
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/argcheck/_info b/ccan/argcheck/_info
new file mode 100644
index 0000000..9a09606
--- /dev/null
+++ b/ccan/argcheck/_info
@@ -0,0 +1,71 @@
+#include "config.h"
+
+/**
+ * argcheck - macros to check arguments at runtime
+ *
+ * This code provides some macros to check arguments for valid value ranges.
+ * Consider this a mild version of assert(3), because all it does is to log
+ * a message and continue.
+ *
+ * These macros don't replace error handling, but they are useful in
+ * situations where an error is unexpected but not common, i.e.
+ * "this shouldn't happen but if it does let me know".
+ *
+ * argcheck's error messages can be disabled by defining ARG_CHECK_DISABLED
+ * before including the header file. The conditions will continue to evaluate
+ * but no error messages will be generated.
+ *
+ * By default, argcheck prints to fprintf(stderr). That can be changed by
+ * #defining ARG_CHECK_LOG_FUNC and #ARG_CHECK_LOG_FUNC_HEADER,
+ * respectively. The latter is the default template with the file/line/func
+ * information. The former is the user-supplied message (if any). All helper
+ * macros have such error messages.
+ *
+ * Macros may evaluate the condition multiple times. Watch out for
+ * side-effects.
+ *
+ * Example:
+ *	#include <stdio.h>
+ *	#include <ccan/argcheck/argcheck.h>
+ *
+ *	enum state { S1, S2, S3 };
+ *
+ *	static int some_state_machine(enum state s) {
+ *	    int b;
+ *
+ *	    arg_check_int_range(s, S1, S3);
+ *
+ *	    switch(s) {
+ *		case S1: b = 8; break;
+ *		case S2: b = 9; break;
+ *		case S3: b = 88; break;
+ *	 	default:
+ *			break;
+ *	    }
+ *
+ *	    return b;
+ *      }
+ *
+ *	int main(int argc, char *argv[]) {
+ *	    int a = S1;
+ *	    return some_state_machine(a);
+ *      }
+ *
+ * Author: Peter Hutterer <peter.hutterer at who-t.net>
+ * Maintainer: Peter Hutterer <peter.hutterer at who-t.net>
+ * License: BSD-MIT
+ */
+
+int main(int argc, char *argv[])
+{
+	/* Expect exactly one argument */
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		printf("ccan/likely\n");
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/ccan/argcheck/argcheck.h b/ccan/argcheck/argcheck.h
new file mode 100644
index 0000000..1010cb2
--- /dev/null
+++ b/ccan/argcheck/argcheck.h
@@ -0,0 +1,198 @@
+/*****************************************************************************
+ *
+ * argcheck - macros for argument value checking
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#ifndef CCAN_ARGCHECK_H
+#define CCAN_ARGCHECK_H
+#include <stdio.h>
+#include <string.h>
+
+#include <ccan/likely/likely.h>
+
+#ifdef ARG_CHECK_DISABLED
+#define arg_check(condition) (condition)
+#define arg_check_msg(condition, ...) (condition)
+#else
+
+#ifndef ARG_CHECK_LOG_FUNC
+/**
+ * ARG_CHECK_LOG_FUNC - function to print error messages (default fprintf)
+ */
+#define ARG_CHECK_LOG_FUNC(...) fprintf(stderr, __VA_ARGS__)
+#endif
+
+#ifndef ARG_CHECK_LOG_FUNC_HEADER
+/**
+ * ARG_CHECK_LOG_FUNC_HEADER - function to print the file, LOC, etc. of the
+ * error (default: same as AC_CHECK_LOG_FUNC)
+ */
+#define ARG_CHECK_LOG_FUNC_HEADER(...) ARG_CHECK_LOG_FUNC(__VA_ARGS__)
+#endif
+
+/**
+ * arg_check - check if the condition is true and print an error if not.
+ *
+ * Example:
+ * static void example(int bar) {
+ *	if (!arg_check(bar == 4))
+ *		return;
+ *	// now assume that bar is 4
+ * }
+ */
+#define arg_check(condition) \
+	(likely(condition) ? 1 : ( \
+		ARG_CHECK_LOG_FUNC_HEADER("argcheck: condition '%s' failed in %s:%d %s()\n", #condition, __FILE__, __LINE__, __func__), 0))
+
+/**
+ * arg_check_msg - check if the condition is true and print an error if is
+ * not, along with a custom message.
+ *
+ * Example:
+ * static void example(int bar) {
+ *	if (!arg_check_msg(bar == 4, "oi, bar is supposed to be 4, not %d\n", bar))
+ *		return;
+ *	// now assume that bar is 4
+ * }
+ */
+#define arg_check_msg(condition, ...) \
+	(likely(condition) ? 1 : ( \
+		ARG_CHECK_LOG_FUNC_HEADER("argcheck: condition '%s' failed in %s:%d %s()\n", #condition, __FILE__, __LINE__, __func__), \
+		ARG_CHECK_LOG_FUNC(__VA_ARGS__), 0))
+
+#endif /* ARG_CHECK_DISABLED */
+
+/**
+ * arg_check_int_eq - check the argument is equal to the value.
+ */
+#define arg_check_int_eq(arg, val) \
+	arg_check_msg(arg == val, "argcheck invalid value: %s (%d) must be %s (%d)\n", #arg, arg, #val, val)
+/**
+ * arg_check_int_ne - check the argument is not equal to the value
+ */
+#define arg_check_int_ne(arg, val) \
+	arg_check_msg(arg != val, "argcheck invalid value: %s must be other than %s (%d)\n", #arg, #val, val)
+/**
+ * arg_check_int_ge - check the argument is equal or greater than the minimum
+ */
+#define arg_check_int_ge(arg, min) \
+	arg_check_msg(arg >= min, "argcheck invalid range: %s is %d, must be >= %s (%d)\n", #arg, arg, #min, min)
+/**
+ * arg_check_int_gt - check the argument is greater than the minimum
+ */
+#define arg_check_int_gt(arg, min) \
+	arg_check_msg(arg > min, "argcheck invalid range: %s is %d, must be > %s (%d)\n", #arg, arg, #min, min)
+/**
+ * arg_check_int_le - check the argument is equal or less than the maximum
+ */
+#define arg_check_int_le(arg, max) \
+	arg_check_msg(arg <= max, "argcheck invalid range: %s is %d, must be <= %s (%d)\n", #arg, arg, #max, max)
+/**
+ * arg_check_int_lt - check the argument is less than the maximum
+ */
+#define arg_check_int_lt(arg, max) \
+	arg_check_msg(arg < max, "argcheck invalid range: %s is %d, must be <= %s (%d)\n", #arg, arg, #max, max)
+/**
+ * arg_check_int_range - check the argument is within a range (inclusive)
+ */
+#define arg_check_int_range(arg, min, max) \
+	(arg_check_msg(min <= max, "argcheck invalid check: %s (%d) must be <= %s (%d)\n", #min, min, #max, max) && \
+	arg_check_msg(arg >= min && arg <= max, "invalid range: range required %d <= %s <= %d, is %d\n", min, #arg, max, arg) )
+/**
+ * arg_check_flag_set - check if a flag is set
+ */
+#define arg_check_flag_set(arg, flag) \
+	(arg_check_int_ne(flag, 0) && \
+	arg_check_msg(((arg) & flag) != 0, "argcheck required flag %s must be set on %s\n", #flag, #arg))
+/**
+ * arg_check_flag_unset - check if a flag is not set
+ */
+#define arg_check_flag_unset(arg, flag) \
+	(arg_check_int_ne(flag, 0) && \
+	arg_check_msg(((arg) & flag) == 0, "argcheck required flag %s must not be set on %s\n", #flag, #arg))
+/**
+ * arg_check_ptr_not_null - check that a pointer is not NULL
+ */
+#define arg_check_ptr_not_null(arg) \
+	arg_check_msg(arg != NULL, "argcheck: %s must not be NULL\n", #arg)
+/**
+ * arg_check_ptr_null - check that a pointer is NULL
+ */
+#define arg_check_ptr_null(arg) \
+	arg_check_msg(arg == NULL, "argcheck: %s must be NULL\n", #arg)
+/**
+ * arg_check_str_null - see arg_check_ptr_null
+ */
+#define arg_check_str_null(arg) \
+	arg_check_ptr_null(arg)
+/**
+ * arg_check_str_not_null - see arg_check_ptr_not_null
+ */
+#define arg_check_str_not_null(arg) \
+	arg_check_ptr_not_null(arg)
+/**
+ * arg_check_str_zero - check that a string is not NULL and of zero length
+ */
+#define arg_check_str_zero(arg) \
+	(arg_check_str_not_null(arg) && \
+	 arg_check_msg(arg[0] == '\0', "argcheck: %s must be of zero length\n", #arg))
+/**
+ * arg_check_str_null_or_zero - check that a string is either NULL or of zero length
+ */
+#define arg_check_str_null_or_zero(arg) \
+	(arg_check_str_null(arg) || arg_check_str_zero(arg))
+/**
+ * arg_check_str_not_zero - check that a string is not NULL and has a length greater than 0
+ */
+#define arg_check_str_not_zero(arg) \
+	(arg_check_str_not_null(arg) && \
+	 arg_check_msg(arg[0] != '\0', "argcheck: %s must not be of zero length\n", #arg))
+/**
+ * arg_check_str_null_or_not_zero - check that a string is either NULL or has a length greater than 0
+ */
+#define arg_check_str_null_or_not_zero(arg) \
+	(arg_check_str_null(arg) || arg_check_str_not_zero(arg))
+/**
+ * arg_check_str_min_len - check that a string is not NULL and has a length greater than or equal to a minimum
+ */
+#define arg_check_str_min_len(arg, min) \
+	(arg_check_str_not_null(arg) && \
+	 arg_check_msg(strlen(arg) >= min, "argcheck: %s (%s)'s length must be >= %s (%d)\n", #arg, arg, #min, min))
+/**
+ * arg_check_str_max_len - check that a string is not NULL and has a length less than or equal to a maximum
+ */
+#define arg_check_str_max_len(arg, max) \
+	(arg_check_str_not_null(arg) && \
+	 arg_check_msg(strlen(arg) <= max, "argcheck: %s (%s)'s length must be >= %s (%d)\n", #arg, arg, #max, max))
+/**
+ * arg_check_str_null_or_min_len - check that a string is NULL or has a length greater than or equal to a minimum
+ */
+#define arg_check_str_null_or_min_len(arg, min) \
+	(arg_check_str_null(arg) || arg_check_str_min_len(arg, min))
+
+/**
+ * arg_check_str_null_or_max_len - check that a string is NULL or has a length less than or equal to a maximum
+ */
+#define arg_check_str_null_or_max_len(arg, max) \
+	(arg_check_str_null(arg) || arg_check_str_max_len(arg, max))
+
+#endif /* CCAN_ARGCHECK_H */
diff --git a/ccan/argcheck/test/run.c b/ccan/argcheck/test/run.c
new file mode 100644
index 0000000..e2d0329
--- /dev/null
+++ b/ccan/argcheck/test/run.c
@@ -0,0 +1,102 @@
+#include <ccan/tap/tap.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <ccan/argcheck/argcheck.h>
+
+int main(void)
+{
+	int a = 0;
+	const int flag = 0x1;
+	const int invalid_flag = 0x0;
+	int *ptr = NULL,
+	    *ptr_not_null = &a;
+
+	const char *str = "hello",
+	           *str_zero = "\0",
+		   *str_null = NULL;
+
+	plan_tests(60);
+
+	ok1(!arg_check_int_eq(a, 1));
+	ok1(arg_check_int_eq(a, 0));
+
+	ok1(!arg_check_int_ne(a, 0));
+	ok1(arg_check_int_ne(a, 10));
+
+	ok1(!arg_check_int_ge(a, 1));
+	ok1(arg_check_int_ge(a, 0));
+	ok1(arg_check_int_ge(a, -1));
+
+	ok1(!arg_check_int_gt(a, 1));
+	ok1(!arg_check_int_gt(a, 0));
+	ok1(arg_check_int_gt(a, -1));
+
+	ok1(!arg_check_int_le(a, -1));
+	ok1(arg_check_int_le(a, 0));
+	ok1(arg_check_int_le(a, 1));
+
+	ok1(!arg_check_int_lt(a, -1));
+	ok1(!arg_check_int_lt(a, 0));
+	ok1(arg_check_int_lt(a, 1));
+
+	ok1(!arg_check_int_range(a, 0, -1));
+	ok1(!arg_check_int_range(a, -3, -1));
+	ok1(arg_check_int_range(a, 0, 1));
+	ok1(arg_check_int_range(a, -1, 0));
+
+	ok1(!arg_check_flag_set(a, invalid_flag));
+	ok1(!arg_check_flag_set(a, flag));
+	ok1(arg_check_flag_set(a | flag, flag));
+
+	ok1(!arg_check_flag_unset(a, invalid_flag));
+	ok1(!arg_check_flag_unset(a | flag, flag));
+	ok1(arg_check_flag_unset(a, flag));
+
+	ok1(arg_check_ptr_null(ptr));
+	ok1(!arg_check_ptr_not_null(ptr));
+	ok1(!arg_check_ptr_null(ptr_not_null));
+	ok1(arg_check_ptr_not_null(ptr_not_null));
+
+	ok1(arg_check_str_null(str_null));
+	ok1(!arg_check_str_not_null(str_null));
+	ok1(!arg_check_str_null(str));
+	ok1(arg_check_str_not_null(str));
+	ok1(!arg_check_str_null(str_zero));
+	ok1(arg_check_str_not_null(str_zero));
+
+	ok1(!arg_check_str_zero(str_null));
+	ok1(arg_check_str_zero(str_zero));
+	ok1(!arg_check_str_zero(str));
+
+	ok1(!arg_check_str_not_zero(str_null));
+	ok1(!arg_check_str_not_zero(str_zero));
+	ok1(arg_check_str_not_zero(str));
+
+	ok1(!arg_check_str_min_len(str_null, 1));
+	ok1(!arg_check_str_min_len(str_zero, 1));
+	ok1(arg_check_str_min_len(str, 1));
+
+	ok1(!arg_check_str_max_len(str_null, 1));
+	ok1(arg_check_str_max_len(str_zero, 1));
+	ok1(!arg_check_str_max_len(str, 1));
+
+	ok1(arg_check_str_null_or_zero(str_null));
+	ok1(arg_check_str_null_or_zero(str_zero));
+	ok1(!arg_check_str_null_or_zero(str));
+
+	ok1(arg_check_str_null_or_not_zero(str_null));
+	ok1(!arg_check_str_null_or_not_zero(str_zero));
+	ok1(arg_check_str_null_or_not_zero(str));
+
+	ok1(arg_check_str_null_or_min_len(str_null, 1));
+	ok1(!arg_check_str_null_or_min_len(str_zero, 1));
+	ok1(arg_check_str_null_or_min_len(str, 1));
+
+	ok1(arg_check_str_null_or_max_len(str_null, 1));
+	ok1(arg_check_str_null_or_max_len(str_zero, 1));
+	ok1(!arg_check_str_null_or_max_len(str, 1));
+
+	return exit_status();
+}
+
-- 
1.8.3.1



More information about the ccan mailing list