First cut, some hacks, three simple modules. init
authorRusty Russell <rusty@vivaldi>
Wed, 5 Dec 2007 13:41:39 +0000 (00:41 +1100)
committerRusty Russell <rusty@vivaldi>
Wed, 5 Dec 2007 13:41:39 +0000 (00:41 +1100)
22 files changed:
.bzrignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
build_assert/_info.c [new file with mode: 0644]
build_assert/build_assert.h [new file with mode: 0644]
build_assert/test/compile_fail-expr.c [new file with mode: 0644]
build_assert/test/compile_fail.c [new file with mode: 0644]
build_assert/test/compile_ok.c [new file with mode: 0644]
build_assert/test/run-EXPR_BUILD_ASSERT.c [new file with mode: 0644]
check_type/_info.c [new file with mode: 0644]
check_type/check_type.h [new file with mode: 0644]
check_type/test/compile_fail-check_type.c [new file with mode: 0644]
check_type/test/compile_fail-check_type_unsigned.c [new file with mode: 0644]
check_type/test/compile_fail-check_types_match.c [new file with mode: 0644]
check_type/test/run.c [new file with mode: 0644]
config.h [new file with mode: 0644]
container_of/_info.c [new file with mode: 0644]
container_of/container_of.h [new file with mode: 0644]
container_of/test/compile_fail-bad-type.c [new file with mode: 0644]
container_of/test/compile_fail-types.c [new file with mode: 0644]
container_of/test/run.c [new file with mode: 0644]
run_tests.c [new file with mode: 0644]
test_all.sh [new file with mode: 0755]

diff --git a/.bzrignore b/.bzrignore
new file mode 100644 (file)
index 0000000..e98cb38
--- /dev/null
@@ -0,0 +1,2 @@
+_info
+.depends
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..a8a6103
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+CFLAGS=-O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I.
+
+ALL=$(patsubst %/test, %, $(wildcard */test))
+ALL_DEPENDS=$(patsubst %, %/.depends, $(ALL))
+
+default: test-all
+
+test-all: $(ALL_DEPENDS)
+       @$(MAKE) `for f in $(ALL); do echo test-$$f test-$$f; while read d; do echo test-$$d test-$$f; done < $$f/.depends; done | tsort`
+
+$(ALL_DEPENDS): %/.depends: %/_info
+       @$< depends > $@ || ( rm -f $@; exit 1 )
+
+test-%: FORCE run_tests
+       @echo Testing $*...
+       @if ./run_tests $* | grep ^'not ok'; then exit 1; else exit 0; fi
+
+FORCE:
+
+run_tests: run_tests.o tap/tap.o talloc/talloc.o 
+
+clean:
+       rm -f run_tests run_tests.o
diff --git a/build_assert/_info.c b/build_assert/_info.c
new file mode 100644 (file)
index 0000000..555aae2
--- /dev/null
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * build_assert - routines for build-time assertions
+ *
+ * This code provides routines which will cause compilation to fail should some
+ * assertion be untrue: such failures are preferable to run-time assertions,
+ * but much more limited since they can only depends on compile-time constants.
+ *
+ * These assertions are most useful when two parts of the code must be kept in
+ * sync: it is better to avoid such cases if possible, but seconds best is to
+ * detect invalid changes at build time.
+ *
+ * For example, a tricky piece of code might rely on a certain element being at
+ * the start of the structure.  To ensure that future changes don't break it,
+ * you would catch such changes in your code like so:
+ *
+ * Example:
+ *     char *foo_string(struct foo *foo)
+ *     {
+ *             // This trick requires that the string be first in the structure
+ *             BUILD_ASSERT(offsetof(struct foo, string) == 0);
+ *             return (char *)foo;
+ *     }
+ */
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0)
+               /* Nothing. */
+               return 0;
+
+       return 1;
+}
diff --git a/build_assert/build_assert.h b/build_assert/build_assert.h
new file mode 100644 (file)
index 0000000..4b0d75e
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef CCAN_BUILD_ASSERT_H
+#define CCAN_BUILD_ASSERT_H
+
+/**
+ * BUILD_ASSERT - assert a build-time dependency.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler.  This can only be used within a function.
+ *
+ * Example:
+ *     char *foo_to_char(struct foo *foo)
+ *     {
+ *             // This code needs string to be at start of foo.
+ *             BUILD_ASSERT(offsetof(struct foo, string) == 0);
+ *             return (char *)foo;
+ *     }
+ */
+#define BUILD_ASSERT(cond) \
+       do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
+
+/**
+ * EXPR_BUILD_ASSERT - assert a build-time dependency, as an expression.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler.  This can be used in an expression: its value is "0".
+ *
+ * Example:
+ *     #define foo_to_char(foo)                                        \
+ *              ((char *)(foo)                                         \
+ *               + EXPR_BUILD_ASSERT(offsetof(struct foo, string) == 0))
+ */
+#define EXPR_BUILD_ASSERT(cond) \
+       (sizeof(char [1 - 2*!(cond)]) - 1)
+
+#endif /* CCAN_BUILD_ASSERT_H */
diff --git a/build_assert/test/compile_fail-expr.c b/build_assert/test/compile_fail-expr.c
new file mode 100644 (file)
index 0000000..41cdc0f
--- /dev/null
@@ -0,0 +1,10 @@
+#include "build_assert/build_assert.h"
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+       return EXPR_BUILD_ASSERT(1 == 0);
+#else
+       return 0;
+#endif
+}
diff --git a/build_assert/test/compile_fail.c b/build_assert/test/compile_fail.c
new file mode 100644 (file)
index 0000000..a6867db
--- /dev/null
@@ -0,0 +1,9 @@
+#include "build_assert/build_assert.h"
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+       BUILD_ASSERT(1 == 0);
+#endif
+       return 0;
+}
diff --git a/build_assert/test/compile_ok.c b/build_assert/test/compile_ok.c
new file mode 100644 (file)
index 0000000..bc5541f
--- /dev/null
@@ -0,0 +1,7 @@
+#include "build_assert/build_assert.h"
+
+int main(int argc, char *argv[])
+{
+       BUILD_ASSERT(1 == 1);
+       return 0;
+}
diff --git a/build_assert/test/run-EXPR_BUILD_ASSERT.c b/build_assert/test/run-EXPR_BUILD_ASSERT.c
new file mode 100644 (file)
index 0000000..7fd0c49
--- /dev/null
@@ -0,0 +1,9 @@
+#include "build_assert/build_assert.h"
+#include "tap/tap.h"
+
+int main(int argc, char *argv[])
+{
+       plan_tests(1);
+       ok1(EXPR_BUILD_ASSERT(1 == 1) == 0);
+       return exit_status();
+}
diff --git a/check_type/_info.c b/check_type/_info.c
new file mode 100644 (file)
index 0000000..06e90eb
--- /dev/null
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * check_type - routines for compile time type checking
+ *
+ * C has fairly weak typing: ints get automatically converted to longs, signed
+ * to unsigned, etc.  There are some cases where this is best avoided, and
+ * these macros provide methods for evoking warnings (or build errors) when
+ * a precise type isn't used.
+ *
+ * On compilers which don't support typeof() these routines are less effective,
+ * since they have to use sizeof() which can only distiguish between types of
+ * different size.
+ */
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+#if !HAVE_TYPEOF
+               printf("build_assert\n");
+#endif
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/check_type/check_type.h b/check_type/check_type.h
new file mode 100644 (file)
index 0000000..e05236f
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef CCAN_CHECK_TYPE_H
+#define CCAN_CHECK_TYPE_H
+#include "config.h"
+
+/**
+ * check_type - issue a warning or build failure if type is not correct.
+ * @expr: the expression whose type we should check (not evaluated).
+ * @type: the exact type we expect the expression to be.
+ *
+ * This macro is usually used within other macros to try to ensure that a macro
+ * argument is of the expected type.  No type promotion of the expression is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_type() always evaluates to 1.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ *     // They should always pass a 64-bit value to _set_some_value!
+ *     #define set_some_value(expr)                    \
+ *             _set_some_value((check_type((expr), uint64_t), (expr)))
+ */
+
+/**
+ * check_types_match - issue a warning or build failure if types are not same.
+ * @expr1: the first expression (not evaluated).
+ * @expr2: the second expression (not evaluated).
+ *
+ * This macro is usually used within other macros to try to ensure that
+ * arguments are of identical types.  No type promotion of the expressions is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_types_match() always evaluates to 1.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ *     // Do subtraction to get to enclosing type, but make sure that
+ *     // pointer is of correct type for that member. 
+ *     #define container_of(mbr_ptr, encl_type, mbr)                   \
+ *             (check_types_match((mbr_ptr), &((encl_type *)0)->mbr),  \
+ *              ((encl_type *)                                         \
+ *               ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr))))
+ */
+#if HAVE_TYPEOF
+#define check_type(expr, type)                 \
+       ((typeof(expr) *)0 != (type *)0)
+
+#define check_types_match(expr1, expr2)                \
+       ((typeof(expr1) *)0 != (typeof(expr2) *)0)
+#else
+#include "build_assert/build_assert.h"
+/* Without typeof, we can only test the sizes. */
+#define check_type(expr, type)                                 \
+       EXPR_BUILD_ASSERT(sizeof(expr) == sizeof(type))
+
+#define check_types_match(expr1, expr2)                                \
+       EXPR_BUILD_ASSERT(sizeof(expr1) == sizeof(expr2))
+#endif /* HAVE_TYPEOF */
+
+#endif /* CCAN_CHECK_TYPE_H */
diff --git a/check_type/test/compile_fail-check_type.c b/check_type/test/compile_fail-check_type.c
new file mode 100644 (file)
index 0000000..d19fe86
--- /dev/null
@@ -0,0 +1,9 @@
+#include "check_type/check_type.h"
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+       check_type(argc, char);
+#endif
+       return 0;
+}
diff --git a/check_type/test/compile_fail-check_type_unsigned.c b/check_type/test/compile_fail-check_type_unsigned.c
new file mode 100644 (file)
index 0000000..6b18acb
--- /dev/null
@@ -0,0 +1,14 @@
+#include "check_type/check_type.h"
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if HAVE_TYPEOF
+       check_type(argc, unsigned int);
+#else
+       /* This doesn't work without typeof, so just fail */
+#error "Fail without typeof"
+#endif
+#endif
+       return 0;
+}
diff --git a/check_type/test/compile_fail-check_types_match.c b/check_type/test/compile_fail-check_types_match.c
new file mode 100644 (file)
index 0000000..bc1f9c3
--- /dev/null
@@ -0,0 +1,10 @@
+#include "check_type/check_type.h"
+
+int main(int argc, char *argv[])
+{
+       unsigned char x = argc;
+#ifdef FAIL
+       check_types_match(argc, x);
+#endif
+       return x;
+}
diff --git a/check_type/test/run.c b/check_type/test/run.c
new file mode 100644 (file)
index 0000000..acfe31e
--- /dev/null
@@ -0,0 +1,22 @@
+#include "check_type/check_type.h"
+#include "tap/tap.h"
+
+int main(int argc, char *argv[])
+{
+       int x = 0, y = 0;
+
+       plan_tests(9);
+
+       ok1(check_type(argc, int) == 0);
+       ok1(check_type(&argc, int *) == 0);
+       ok1(check_types_match(argc, argc) == 0);
+       ok1(check_types_match(argc, x) == 0);
+       ok1(check_types_match(&argc, &x) == 0);
+
+       ok1(check_type(x++, int) == 0);
+       ok(x == 0, "check_type does not evaluate expression");
+       ok1(check_types_match(x++, y++) == 0);
+       ok(x == 0 && y == 0, "check_types_match does not evaluate expressions");
+
+       return 0;
+}
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..6cde6a4
--- /dev/null
+++ b/config.h
@@ -0,0 +1,3 @@
+/* Simple config.h for gcc. */
+#define HAVE_TYPEOF 1
+#define HAVE_STATEMENT_EXPR 1
diff --git a/container_of/_info.c b/container_of/_info.c
new file mode 100644 (file)
index 0000000..7705e38
--- /dev/null
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * container_of - routine for upcasting
+ *
+ * It is often convenient to create code where the caller registers a pointer
+ * to a generic structure and a callback.  The callback might know that the
+ * pointer points to within a larger structure, and container_of gives a
+ * convenient and fairly type-safe way of returning to the enclosing structure.
+ *
+ * This idiom is an alternative to providing a void * pointer for every
+ * callback.
+ *
+ * Example:
+ *     struct info
+ *     {
+ *             int my_stuff;
+ *             struct timer timer;
+ *     };
+ *
+ *     static void my_timer_callback(struct timer *timer)
+ *     {
+ *             struct info *info = container_of(timer, struct info, timer);
+ *             printf("my_stuff is %u\n", info->my_stuff);
+ *     }
+ *
+ *     int main()
+ *     {
+ *             struct info info = { .my_stuff = 1 };
+ *
+ *             register_timer(&info.timer);
+ *             ...
+ */
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("check_type\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/container_of/container_of.h b/container_of/container_of.h
new file mode 100644 (file)
index 0000000..49f20b5
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef CCAN_CONTAINER_OF_H
+#define CCAN_CONTAINER_OF_H
+#include <stddef.h>
+
+#include "config.h"
+#include "check_type/check_type.h"
+
+#define container_of(member_ptr, containing_type, member)              \
+        ((containing_type *)                                           \
+         ((char *)(member_ptr) - offsetof(containing_type, member))    \
+         - check_types_match(*(member_ptr), ((containing_type *)0)->member))
+
+
+#endif /* CCAN_CONTAINER_OF_H */
diff --git a/container_of/test/compile_fail-bad-type.c b/container_of/test/compile_fail-bad-type.c
new file mode 100644 (file)
index 0000000..d372fa5
--- /dev/null
@@ -0,0 +1,21 @@
+#include "container_of/container_of.h"
+#include <stdlib.h>
+
+struct foo {
+       int a;
+       char b;
+};
+
+int main(int argc, char *argv[])
+{
+       struct foo foo = { .a = 1, .b = 2 };
+       int *intp = &foo.a;
+       char *p;
+
+#ifdef FAIL
+       p = container_of(intp, struct foo, a);
+#else
+       p = (char *)intp;
+#endif
+       return p == NULL;
+}
diff --git a/container_of/test/compile_fail-types.c b/container_of/test/compile_fail-types.c
new file mode 100644 (file)
index 0000000..33d9878
--- /dev/null
@@ -0,0 +1,20 @@
+#include "container_of/container_of.h"
+#include <stdlib.h>
+
+struct foo {
+       int a;
+       char b;
+};
+
+int main(int argc, char *argv[])
+{
+       struct foo foo = { .a = 1, .b = 2 }, *foop;
+       int *intp = &foo.a;
+
+#ifdef FAIL
+       foop = container_of(intp, struct foo, b);
+#else
+       foop = NULL;
+#endif
+       return intp == NULL;
+}
diff --git a/container_of/test/run.c b/container_of/test/run.c
new file mode 100644 (file)
index 0000000..dab462b
--- /dev/null
@@ -0,0 +1,19 @@
+#include "container_of/container_of.h"
+#include "tap/tap.h"
+
+struct foo {
+       int a;
+       char b;
+};
+
+int main(int argc, char *argv[])
+{
+       struct foo foo = { .a = 1, .b = 2 };
+       int *intp = &foo.a;
+       char *charp = &foo.b;
+
+       plan_tests(2);
+       ok1(container_of(intp, struct foo, a) == &foo);
+       ok1(container_of(charp, struct foo, b) == &foo);
+       return exit_status();
+}
diff --git a/run_tests.c b/run_tests.c
new file mode 100644 (file)
index 0000000..15b52ac
--- /dev/null
@@ -0,0 +1,210 @@
+#include <err.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <assert.h>
+#include <unistd.h>
+#include "tap/tap.h"
+#include "talloc/talloc.h"
+#include "string/string.h"
+
+#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I."
+
+/* FIXME: Use build bug later. */
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static struct test *tests = NULL;
+static struct obj *objs = NULL;
+static int verbose;
+
+struct test_type
+{
+       const char *name;
+       void (*testfn)(struct test_type *t, const char *name);
+};
+
+struct test
+{
+       struct test *next;
+       struct test_type *type;
+       char *name;
+};
+
+struct obj
+{
+       struct obj *next;
+       char *name;
+};
+
+static char *output_name(const char *name)
+{
+       char *ret;
+
+       assert(strends(name, ".c"));
+
+       ret = talloc_strdup(name, name);
+       ret[strlen(ret) - 2] = '\0';
+       return ret;
+}
+
+static char *obj_list(void)
+{
+       char *list = talloc_strdup(objs, "");
+       struct obj *i;
+
+       for (i = objs; i; i = i->next)
+               list = talloc_asprintf_append(list, "%s ", i->name);
+
+       /* FIXME */
+       list = talloc_asprintf_append(list, "tap/tap.o");
+       return list;
+}
+
+static void compile_objs(void)
+{
+       struct obj *i;
+
+       for (i = objs; i; i = i->next) {
+               char *cmd = talloc_asprintf(i, "gcc " CFLAGS " -o %s.o -c %s%s",
+                                           output_name(i->name), i->name,
+                                           verbose ? "" : "> /dev/null 2>&1");
+               ok(system(cmd) == 0, "%s", cmd);
+       }
+}
+
+static void cleanup_objs(void)
+{
+       struct obj *i;
+
+       for (i = objs; i; i = i->next)
+               unlink(talloc_asprintf(i, "%s.o", output_name(i->name)));
+}
+
+static void add_test(const char *testdir, const char *name, struct test_type *t)
+{
+       struct test *test = talloc(testdir, struct test);
+
+       test->next = tests;
+       test->type = t;
+       test->name = talloc_asprintf(test, "%s/%s", testdir, name);
+       tests = test;
+}
+
+static void add_obj(const char *testdir, const char *name)
+{
+       struct obj *obj = talloc(testdir, struct obj);
+
+       obj->next = objs;
+       obj->name = talloc_asprintf(obj, "%s/%s", testdir, name);
+       objs = obj;
+}
+
+static int build(const char *name, int fail)
+{
+       const char *cmd;
+       int ret;
+
+       cmd = talloc_asprintf(name, "gcc " CFLAGS " %s -o %s %s %s%s",
+                             fail ? "-DFAIL" : "",
+                             output_name(name), name, obj_list(),
+                             verbose ? "" : "> /dev/null 2>&1");
+
+       if (verbose)
+               fprintf(stderr, "Running %s\n", cmd);
+
+       ret = system(cmd);
+       if (ret == -1)
+               diag("cmd '%s' failed to execute", cmd);
+
+       return ret;
+}
+
+static void compile_ok(struct test_type *t, const char *name)
+{
+       ok(build(name, 0) == 0, "%s %s", t->name, name);
+}
+
+static void compile_fail(struct test_type *t, const char *name)
+{
+       if (build(name, 0) != 0)
+               fail("non-FAIL build %s", name);
+       else
+               ok(build(name, 1) > 0, "%s %s", t->name, name);
+}
+
+static void run(const char *name)
+{
+       if (system(output_name(name)) == -1)
+               fail("running %s had error %m", name);
+}
+
+static void cleanup(const char *name)
+{
+       unlink(output_name(name));
+}
+
+static struct test_type test_types[] = {
+       { "compile_ok", compile_ok },
+       { "compile_fail", compile_fail },
+       { "run", compile_ok },
+};
+
+int main(int argc, char *argv[])
+{
+       DIR *dir;
+       struct dirent *d;
+       char *testdir;
+       struct test *test;
+       unsigned int num_tests = 0, num_objs = 0;
+
+       if (argc > 1 && streq(argv[1], "--verbose")) {
+               verbose = 1;
+               argc--;
+               argv++;
+       }
+
+       if (argc != 2)
+               errx(1, "Usage: run_tests [--verbose] <dir>");
+
+       testdir = talloc_asprintf(NULL, "%s/test", argv[1]);
+       dir = opendir(testdir);
+       if (!dir)
+               err(1, "Opening '%s'", testdir);
+
+       while ((d = readdir(dir)) != NULL) {
+               unsigned int i;
+               if (d->d_name[0] == '.' || !strends(d->d_name, ".c"))
+                       continue;
+
+               for (i = 0; i < ARRAY_SIZE(test_types); i++) {
+                       if (strstarts(d->d_name, test_types[i].name)) {
+                               add_test(testdir, d->d_name, &test_types[i]);
+                               num_tests++;
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(test_types)) {
+                       add_obj(testdir, d->d_name);
+                       num_objs++;
+               }
+       }
+
+       plan_tests(num_tests + num_objs);
+       /* First all the extra object compilations. */
+       compile_objs();
+
+       /* Do all the test compilations. */
+       for (test = tests; test; test = test->next)
+               test->type->testfn(test->type, test->name);
+
+       cleanup_objs();
+
+       /* Now run all the ones which wanted to run. */
+       for (test = tests; test; test = test->next) {
+               if (streq(test->type->name, "run"))
+                       run(test->name);
+               cleanup(test->name);
+       }
+
+       exit(exit_status());
+}
diff --git a/test_all.sh b/test_all.sh
new file mode 100755 (executable)
index 0000000..3c65c21
--- /dev/null
@@ -0,0 +1,22 @@
+#! /bin/sh
+
+# First, test normal config.
+if ! make -s; then
+    echo Normal config failed.
+    exit 1
+fi
+
+# Now, remove one HAVE_ at a time.
+cp config.h original-config.h
+trap "mv original-config.h config.h && rm -f .newconfig" EXIT
+
+while grep -q '1$' config.h; do
+    tr '\012' @ < config.h | sed 's/1@/0@/' | tr @ '\012' > .newconfig
+    diff -u config.h .newconfig
+    mv .newconfig config.h
+    if ! make -s; then
+       echo Failed config:
+       cat config.h
+       exit 1
+    fi
+done