aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2016-04-19 14:10:13 +0200
committerMarc André Tanner <mat@brain-dump.org>2016-04-19 14:10:13 +0200
commit2e102a11afe9b256f0f9cf5391faf445fd40430a (patch)
treefcf8f6f4e2b7832bed9c380bf49bcc491ca06292
downloadvis-2e102a11afe9b256f0f9cf5391faf445fd40430a.tar.gz
vis-2e102a11afe9b256f0f9cf5391faf445fd40430a.tar.xz
Initial import of test branch from vis repository
-rw-r--r--Makefile16
-rw-r--r--README.md14
-rw-r--r--core/.gitignore5
-rw-r--r--core/Makefile42
-rw-r--r--core/README.md9
-rw-r--r--core/array.c137
-rw-r--r--core/buffer.c61
-rw-r--r--core/ccan-config.c570
l---------core/ccan/compiler/LICENSE1
-rw-r--r--core/ccan/compiler/_info64
-rw-r--r--core/ccan/compiler/compiler.h231
-rw-r--r--core/ccan/compiler/test/compile_fail-printf.c22
-rw-r--r--core/ccan/compiler/test/run-is_compile_constant.c15
-rw-r--r--core/ccan/tap/_info61
-rw-r--r--core/ccan/tap/tap.3362
-rw-r--r--core/ccan/tap/tap.c457
-rw-r--r--core/ccan/tap/tap.h251
-rw-r--r--core/ccan/tap/test/run.c133
-rw-r--r--core/licenses/CC028
-rw-r--r--core/map.c106
-rw-r--r--core/text.c119
-rw-r--r--util/.gitignore1
-rw-r--r--util/Makefile23
-rw-r--r--util/README.md11
-rw-r--r--util/keys.c161
-rw-r--r--util/test1
-rw-r--r--vim/.gitignore3
-rw-r--r--vim/Makefile15
-rw-r--r--vim/README.md16
-rw-r--r--vim/golf/4d1a34ccfa85f32065000004/1.in13
-rw-r--r--vim/golf/4d1a34ccfa85f32065000004/1.keys6
-rw-r--r--vim/golf/4d1a34ccfa85f32065000004/1.ref13
-rw-r--r--vim/golf/4d1ac1800a045132c0000011/1.in2
-rw-r--r--vim/golf/4d1ac1800a045132c0000011/1.keys9
-rw-r--r--vim/golf/4d1ac1800a045132c0000011/1.ref7
-rw-r--r--vim/golf/4d665abd7d73e02a55000009/1.in3
-rw-r--r--vim/golf/4d665abd7d73e02a55000009/1.keys.broken16
-rw-r--r--vim/golf/4d665abd7d73e02a55000009/1.ref14
-rw-r--r--vim/golf/4ddbd92898957e0001000016/1.in24
-rw-r--r--vim/golf/4ddbd92898957e0001000016/1.keys20
-rw-r--r--vim/golf/4ddbd92898957e0001000016/1.ref24
-rw-r--r--vim/golf/4e31627b74ab580001000007/1.in28
-rw-r--r--vim/golf/4e31627b74ab580001000007/1.keys.broken21
-rw-r--r--vim/golf/4e31627b74ab580001000007/1.ref26
-rw-r--r--vim/golf/4ef209ef78702b0001000019/1.in19
-rw-r--r--vim/golf/4ef209ef78702b0001000019/1.keys6
-rw-r--r--vim/golf/4ef209ef78702b0001000019/1.ref29
-rw-r--r--vim/golf/4fbf8e303be58b0001000024/1.in1
-rw-r--r--vim/golf/4fbf8e303be58b0001000024/1.keys.broken17
-rw-r--r--vim/golf/4fbf8e303be58b0001000024/1.ref32
-rw-r--r--vim/golf/50ad2cb165b8db0002000029/1.in18
-rw-r--r--vim/golf/50ad2cb165b8db0002000029/1.keys.broken12
-rw-r--r--vim/golf/50ad2cb165b8db0002000029/1.ref7
-rw-r--r--vim/golf/50ae009b65b8db0002000047/1.in4
-rw-r--r--vim/golf/50ae009b65b8db0002000047/1.keys7
-rw-r--r--vim/golf/50ae009b65b8db0002000047/1.ref4
-rw-r--r--vim/golf/50b1d7239aad89000200002d/1.in10
-rw-r--r--vim/golf/50b1d7239aad89000200002d/1.ref1
-rw-r--r--vim/golf/50c13afab855760002000049/1.in24
-rw-r--r--vim/golf/50c13afab855760002000049/1.ref24
-rw-r--r--vim/golf/50c18a08b855760002000056/1.in162
-rw-r--r--vim/golf/50c18a08b855760002000056/1.keys.broken8
-rw-r--r--vim/golf/50c18a08b855760002000056/1.ref162
-rw-r--r--vim/golf/50c2c246b0544c000200003f/1.in11
-rw-r--r--vim/golf/50c2c246b0544c000200003f/1.ref11
-rw-r--r--vim/golf/50d0c33daa503f000200000f/1.in1
-rw-r--r--vim/golf/50d0c33daa503f000200000f/1.ref1
-rw-r--r--vim/golf/50ee7504c0e3aa0002000040/1.in1
-rw-r--r--vim/golf/50ee7504c0e3aa0002000040/1.ref25
-rw-r--r--vim/golf/51103ad8041832000200003f/1.in1
-rw-r--r--vim/golf/51103ad8041832000200003f/1.ref21
-rw-r--r--vim/golf/5192f96ad8df110002000002/1.in2
-rw-r--r--vim/golf/5192f96ad8df110002000002/1.ref2
-rw-r--r--vim/golf/524e1a20b81fe50002000008/1.in3
-rw-r--r--vim/golf/524e1a20b81fe50002000008/1.ref8
-rw-r--r--vim/golf/52c3cb0d9b8634000200000e/1.in1
-rw-r--r--vim/golf/52c3cb0d9b8634000200000e/1.ref1
-rw-r--r--vim/golf/53369b712a09c1000223fb57/1.in15
-rw-r--r--vim/golf/53369b712a09c1000223fb57/1.ref15
-rw-r--r--vim/golf/536cfa23fcccd100025678bd/1.in1
-rw-r--r--vim/golf/536cfa23fcccd100025678bd/1.ref2
-rw-r--r--vim/golf/537a553282aa3e000222048a/1.in14
-rw-r--r--vim/golf/537a553282aa3e000222048a/1.ref14
-rw-r--r--vim/golf/53d93fc3768e280002124f23/1.in1
-rw-r--r--vim/golf/53d93fc3768e280002124f23/1.ref1
-rw-r--r--vim/golf/54595b13128576000257a3c1/1.in6
-rw-r--r--vim/golf/54595b13128576000257a3c1/1.ref6
-rw-r--r--vim/golf/5462e3f41198b80002512673/1.in3
-rw-r--r--vim/golf/5462e3f41198b80002512673/1.ref3
-rw-r--r--vim/golf/54698da795f6da00020d85ed/1.in6
-rw-r--r--vim/golf/54698da795f6da00020d85ed/1.ref3
-rw-r--r--vim/golf/54862fbb3f90ac0002904cf5/1.in8
-rw-r--r--vim/golf/54862fbb3f90ac0002904cf5/1.ref3
-rw-r--r--vim/golf/55771cc4750ef86573003b83/1.in11
-rw-r--r--vim/golf/55771cc4750ef86573003b83/1.ref11
-rw-r--r--vim/golf/559c30948ef59c0eb7000002/1.in5
-rw-r--r--vim/golf/559c30948ef59c0eb7000002/1.ref1
-rw-r--r--vim/golf/55b18bbea9c2c30d04000001/1.in10
-rw-r--r--vim/golf/55b18bbea9c2c30d04000001/1.ref13
-rw-r--r--vim/golf/55bcdc3ef4219f456102374f/1.in1
-rw-r--r--vim/golf/55bcdc3ef4219f456102374f/1.ref1
-rw-r--r--vim/golf/55f9720b4a665c2acf0008c8/1.in4
-rw-r--r--vim/golf/55f9720b4a665c2acf0008c8/1.ref8
-rw-r--r--vim/operators/case/case.in1
-rw-r--r--vim/operators/case/case.keys1
-rw-r--r--vim/operators/change/change.in1
-rw-r--r--vim/operators/change/change.keys1
-rw-r--r--vim/operators/delete/delete.in5
-rw-r--r--vim/operators/delete/delete.keys2
-rw-r--r--vim/operators/join/join.in5
-rw-r--r--vim/operators/join/join.keys1
-rw-r--r--vim/operators/put/put.in1
-rw-r--r--vim/operators/put/put.keys1
-rw-r--r--vim/operators/shift/shift.in2
-rw-r--r--vim/operators/shift/shift.keys1
-rw-r--r--vim/operators/swap/swap.in3
-rw-r--r--vim/operators/swap/swap.keys1
-rw-r--r--vim/operators/yank/yank.in1
-rw-r--r--vim/operators/yank/yank.keys1
-rw-r--r--vim/prompt/history.in3
-rw-r--r--vim/prompt/history.keys6
-rwxr-xr-xvim/test.sh57
-rw-r--r--vim/text-objects/braces-count-linewise.in5
-rw-r--r--vim/text-objects/braces-count-linewise.keys2
-rw-r--r--vis/.gitignore3
-rw-r--r--vis/Makefile15
-rw-r--r--vis/README.md17
-rw-r--r--vis/multiple-cursors/basic.in4
-rw-r--r--vis/multiple-cursors/basic.keys6
-rw-r--r--vis/multiple-cursors/basic.ref4
-rwxr-xr-xvis/test.sh35
131 files changed, 4148 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b4c1921
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+test:
+ @$(MAKE) -C core
+ @$(MAKE) -C vim
+ @$(MAKE) -C vis
+
+test-local: keys-local test
+
+keys-local:
+ @$(MAKE) -C util $@
+
+clean:
+ @$(MAKE) -C core clean
+ @$(MAKE) -C vim clean
+ @$(MAKE) -C vis clean
+
+.PHONY: test test-local keys-local clean
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..06bc403
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+Testing Infrastructure for Vis
+------------------------------
+
+This repository contains testing infrastructure for the
+[vis editor](https://github.com/martanne/vis). It is expected
+to be cloned into a sub directory of the `vis` source tree.
+
+There exist 3 different kinds of tests:
+
+ * `core` are C unit tests for core data structures used by vis
+ * `vim` tests vim compatibility
+ * `vis` contains tests for vis specific behavior/features
+
+Run `make` to execute all test suites.
diff --git a/core/.gitignore b/core/.gitignore
new file mode 100644
index 0000000..486e08a
--- /dev/null
+++ b/core/.gitignore
@@ -0,0 +1,5 @@
+/config.h
+/text
+/buffer
+/map
+/array
diff --git a/core/Makefile b/core/Makefile
new file mode 100644
index 0000000..468d260
--- /dev/null
+++ b/core/Makefile
@@ -0,0 +1,42 @@
+-include ../../config.mk
+
+SRC = $(wildcard ccan/*/*.c)
+CFLAGS += -I. -I../..
+
+test: text buffer map array
+ @./text
+ @./buffer
+ @./map
+ @./array
+
+config.h:
+ @echo Generating ccan configuration header
+ @${CC} ccan-config.c && ./a.out > config.h && rm -f a.out
+
+text: config.h text.c ../../text.c ../../text-util.c
+ @echo Compiling $@ binary
+ @${CC} ${CFLAGS} ${CFLAGS_STD} ${filter %.c, $^} ${SRC} ${LDFLAGS} -o $@
+
+buffer: config.h buffer.c ../../buffer.c
+ @echo Compiling $@ binary
+ @${CC} ${CFLAGS} ${CFLAGS_STD} ${filter %.c, $^} ${SRC} ${LDFLAGS} -o $@
+
+map: config.h map.c ../../map.c
+ @echo Compiling $@ binary
+ @${CC} ${CFLAGS} ${CFLAGS_STD} ${filter %.c, $^} ${SRC} ${LDFLAGS} -o $@
+
+array: config.h array.c ../../array.c
+ @echo Compiling $@ binary
+ @${CC} ${CFLAGS} ${CFLAGS_STD} ${filter %.c, $^} ${SRC} ${LDFLAGS} -o $@
+
+debug: clean
+ $(MAKE) CFLAGS_VIS='${DEBUG_CFLAGS_VIS}'
+
+clean:
+ @echo cleaning
+ @rm -f text
+ @rm -f buffer
+ @rm -f map
+ @rm -f array
+
+.PHONY: clean debug
diff --git a/core/README.md b/core/README.md
new file mode 100644
index 0000000..849d7f7
--- /dev/null
+++ b/core/README.md
@@ -0,0 +1,9 @@
+Unit tests for the low level routines used by vis
+-------------------------------------------------
+
+The testing infrastruce makes use of the tap module from the
+[C Code Archive](http://ccodearchive.net).
+
+To run the tests, execute `make`.
+
+ $ make
diff --git a/core/array.c b/core/array.c
new file mode 100644
index 0000000..5f4f507
--- /dev/null
+++ b/core/array.c
@@ -0,0 +1,137 @@
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include "array.h"
+#include "../../util.h"
+
+typedef struct {
+ char key[64];
+ int value;
+} Item;
+
+static int values[] = { 2, 3, 5, 7, 11 };
+static const size_t len = LENGTH(values);
+
+static bool item_compare(Item *a, Item *b) {
+ return strcmp(a->key, b->key) == 0 && a->value == b->value;
+}
+
+static void test_small_objects(void) {
+ Array arr;
+ array_init_sized(&arr, sizeof(int));
+ ok(array_length(&arr) == 0, "Initialization");
+ ok(!array_set(&arr, 0, NULL) && errno == EINVAL, "Set with invalid index");
+ ok(array_get(&arr, 0) == NULL && errno == EINVAL, "Get with invalid index");
+
+ for (size_t i = 0; i < len; i++) {
+ int *v;
+ ok(array_add(&arr, &values[i]) && array_length(&arr) == i+1,
+ "Add integer: %zu = %d", i, values[i]);
+ ok((v = array_get(&arr, i)) && *v == values[i],
+ "Get integer: %zu = %d", i, *v);
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ ok(array_set(&arr, i, &values[len-i-1]) && array_length(&arr) == len,
+ "Set array element: %zu = %d", i, values[len-i-1]);
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ int *v;
+ ok((v = array_get(&arr, i)) && *v == values[len-i-1],
+ "Get array element: %zu = %d", i, *v);
+ }
+
+ array_clear(&arr);
+ ok(array_length(&arr) == 0 && array_get(&arr, 0) == NULL && errno == EINVAL, "Clear");
+}
+
+static void test_large_objects(void) {
+ Array arr;
+ array_init_sized(&arr, sizeof(Item));
+ ok(array_length(&arr) == 0 && array_get(&arr, 0) == NULL && errno == EINVAL,
+ "Initialization");
+
+ Item items[len];
+
+ for (size_t i = 0; i < len; i++) {
+ snprintf(items[i].key, sizeof items[i].key, "key: %zu", i);
+ items[i].value = values[i];
+ Item *item;
+ ok(array_add(&arr, &items[i]) && array_length(&arr) == i+1,
+ "Add item: %zu = { '%s' = %d }", i, items[i].key, items[i].value);
+ ok((item = array_get(&arr, i)) && item != &items[i] && item_compare(item, &items[i]),
+ "Get item: %zu = { '%s' = %d }", i, item->key, item->value);
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ Item *item = &items[len-i-1];
+ ok(array_set(&arr, i, item) && array_length(&arr) == len,
+ "Set array element: %zu = { '%s' = %d }", i, item->key, item->value);
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ Item *item;
+ ok((item = array_get(&arr, i)) && item != &items[len-i-1] && item_compare(item, &items[len-i-1]),
+ "Get item: %zu = { '%s' = %d }", i, item->key, item->value);
+ }
+
+ ok(!array_add_ptr(&arr, &items[0]) && errno == ENOTSUP && array_length(&arr) == len,
+ "Adding pointer to non pointer array");
+ ok(!array_set_ptr(&arr, 0, &items[0]) && errno == ENOTSUP && item_compare(array_get(&arr, 0), &items[len-1]),
+ "Setting pointer in non pointer array");
+
+ array_clear(&arr);
+ ok(array_length(&arr) == 0 && array_get(&arr, 0) == NULL && errno == EINVAL, "Clear");
+}
+
+static void test_pointers(void) {
+
+ Array arr;
+ array_init(&arr);
+ ok(array_length(&arr) == 0 && array_get_ptr(&arr, 0) == NULL && errno == EINVAL,
+ "Initialization");
+
+ Item *items[len];
+
+ for (size_t i = 0; i < len; i++) {
+ items[i] = malloc(sizeof(Item));
+
+ snprintf(items[i]->key, sizeof(items[i]->key), "key: %zu", i);
+ items[i]->value = values[i];
+ Item *item;
+ ok(array_add_ptr(&arr, items[i]) && array_length(&arr) == i+1,
+ "Add item: %zu = %p", i, items[i]);
+ ok((item = array_get_ptr(&arr, i)) && item == items[i],
+ "Get item: %zu = %p", i, item);
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ Item *item = items[len-i-1];
+ ok(array_set_ptr(&arr, i, item) && array_length(&arr) == len,
+ "Set item: %zu = %p", i, item);
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ Item *item;
+ ok((item = array_get_ptr(&arr, i)) && item == items[len-i-1],
+ "Get item: %zu = %p", i, item);
+ }
+
+ array_clear(&arr);
+ ok(array_length(&arr) == 0 && array_get_ptr(&arr, 0) == NULL && errno == EINVAL, "Clear");
+}
+
+int main(int argc, char *argv[]) {
+ plan_no_plan();
+
+ test_small_objects();
+ test_large_objects();
+ test_pointers();
+
+ return exit_status();
+}
diff --git a/core/buffer.c b/core/buffer.c
new file mode 100644
index 0000000..d3ada14
--- /dev/null
+++ b/core/buffer.c
@@ -0,0 +1,61 @@
+#include <ccan/tap/tap.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include "buffer.h"
+
+static bool compare(Buffer *buf, const char *data, size_t len) {
+ return buf->len == len && memcmp(buf->data, data, buf->len) == 0;
+}
+
+static bool compare0(Buffer *buf, const char *data) {
+ return buf->len == strlen(data)+1 && memcmp(buf->data, data, buf->len) == 0;
+}
+
+int main(int argc, char *argv[]) {
+ Buffer buf;
+
+ plan_no_plan();
+
+ buffer_init(&buf);
+ ok(buf.data == NULL && buf.len == 0 && buf.size == 0, "Initialization");
+ ok(!buffer_insert0(&buf, 1, "foo"), "Insert string at invalid position");
+
+ ok(buffer_insert0(&buf, 0, "") && compare0(&buf, ""), "Insert empty string");
+ ok(buffer_insert0(&buf, 0, "foo") && compare0(&buf, "foo"), "Insert string at start");
+ ok(buffer_insert0(&buf, 1, "l") && compare0(&buf, "floo"), "Insert string in middle");
+ ok(buffer_insert0(&buf, 4, "r") && compare0(&buf, "floor"), "Insert string at end");
+
+ ok(buffer_put0(&buf, "") && compare0(&buf, ""), "Put empty string");
+ ok(buffer_put0(&buf, "bar") && compare0(&buf, "bar"), "Put string");
+
+ ok(buffer_prepend0(&buf, "foo") && compare0(&buf, "foobar"), "Prepend string");
+ ok(buffer_append0(&buf, "baz") && compare0(&buf, "foobarbaz"), "Append string");
+
+ buffer_release(&buf);
+ ok(buf.data == NULL && buf.len == 0 && buf.size == 0, "Release");
+
+ ok(buffer_insert(&buf, 0, "foo", 0) && compare(&buf, "", 0), "Insert zero length data");
+ ok(buffer_insert(&buf, 0, "foo", 3) && compare(&buf, "foo", 3), "Insert data at start");
+ ok(buffer_insert(&buf, 1, "l", 1) && compare(&buf, "floo", 4), "Insert data in middle");
+ ok(buffer_insert(&buf, 4, "r", 1) && compare(&buf, "floor", 5), "Insert data at end");
+
+ size_t size = buf.size;
+ buffer_truncate(&buf);
+ ok(buf.len == 0 && buf.data && buf.size == size, "Truncate");
+
+ ok(buffer_put(&buf, "foo", 0) && compare(&buf, "", 0), "Put zero length data");
+ ok(buffer_put(&buf, "bar", 3) && compare(&buf, "bar", 3), "Put data");
+
+ ok(buffer_prepend(&buf, "foo\0", 4) && compare(&buf, "foo\0bar", 7), "Prepend data");
+ ok(buffer_append(&buf, "\0baz", 4) && compare(&buf, "foo\0bar\0baz", 11), "Append data");
+
+ ok(buffer_grow(&buf, size+1) && compare(&buf, "foo\0bar\0baz", 11) && buf.size >= size+1, "Grow");
+
+ size = buf.size;
+ buffer_clear(&buf);
+ ok(buf.len == 0 && buf.data && buf.size == size, "Clear");
+
+ return exit_status();
+}
diff --git a/core/ccan-config.c b/core/ccan-config.c
new file mode 100644
index 0000000..f4edb8e
--- /dev/null
+++ b/core/ccan-config.c
@@ -0,0 +1,570 @@
+/* Simple tool to create config.h.
+ * Would be much easier with ccan modules, but deliberately standalone.
+ *
+ * Copyright 2011 Rusty Russell <rusty@rustcorp.com.au>. MIT license.
+ *
+ * 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.
+ */
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define DEFAULT_COMPILER "cc"
+#define DEFAULT_FLAGS "-g3 -ggdb -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition"
+
+#define OUTPUT_FILE "configurator.out"
+#define INPUT_FILE "configuratortest.c"
+
+static int verbose;
+
+enum test_style {
+ OUTSIDE_MAIN = 0x1,
+ DEFINES_FUNC = 0x2,
+ INSIDE_MAIN = 0x4,
+ DEFINES_EVERYTHING = 0x8,
+ MAY_NOT_COMPILE = 0x10,
+ EXECUTE = 0x8000
+};
+
+struct test {
+ const char *name;
+ enum test_style style;
+ const char *depends;
+ const char *link;
+ const char *fragment;
+ const char *overrides; /* On success, force this to '1' */
+ bool done;
+ bool answer;
+};
+
+static struct test tests[] = {
+ { "HAVE_32BIT_OFF_T", DEFINES_EVERYTHING|EXECUTE, NULL, NULL,
+ "#include <sys/types.h>\n"
+ "int main(int argc, char *argv[]) {\n"
+ " return sizeof(off_t) == 4 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_ALIGNOF", INSIDE_MAIN, NULL, NULL,
+ "return __alignof__(double) > 0 ? 0 : 1;" },
+ { "HAVE_ASPRINTF", DEFINES_FUNC, NULL, NULL,
+ "#define _GNU_SOURCE\n"
+ "#include <stdio.h>\n"
+ "static char *func(int x) {"
+ " char *p;\n"
+ " if (asprintf(&p, \"%u\", x) == -1) p = NULL;"
+ " return p;\n"
+ "}" },
+ { "HAVE_ATTRIBUTE_COLD", DEFINES_FUNC, NULL, NULL,
+ "static int __attribute__((cold)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_CONST", DEFINES_FUNC, NULL, NULL,
+ "static int __attribute__((const)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_PURE", DEFINES_FUNC, NULL, NULL,
+ "static int __attribute__((pure)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_MAY_ALIAS", OUTSIDE_MAIN, NULL, NULL,
+ "typedef short __attribute__((__may_alias__)) short_a;" },
+ { "HAVE_ATTRIBUTE_NORETURN", DEFINES_FUNC, NULL, NULL,
+ "#include <stdlib.h>\n"
+ "static void __attribute__((noreturn)) func(int x) { exit(x); }" },
+ { "HAVE_ATTRIBUTE_PRINTF", DEFINES_FUNC, NULL, NULL,
+ "static void __attribute__((format(__printf__, 1, 2))) func(const char *fmt, ...) { }" },
+ { "HAVE_ATTRIBUTE_UNUSED", OUTSIDE_MAIN, NULL, NULL,
+ "static int __attribute__((unused)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_USED", OUTSIDE_MAIN, NULL, NULL,
+ "static int __attribute__((used)) func(int x) { return x; }" },
+ { "HAVE_BACKTRACE", DEFINES_FUNC, NULL, NULL,
+ "#include <execinfo.h>\n"
+ "static int func(int x) {"
+ " void *bt[10];\n"
+ " return backtrace(bt, 10) < x;\n"
+ "}" },
+ { "HAVE_BIG_ENDIAN", INSIDE_MAIN|EXECUTE, NULL, NULL,
+ "union { int i; char c[sizeof(int)]; } u;\n"
+ "u.i = 0x01020304;\n"
+ "return u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04 ? 0 : 1;" },
+ { "HAVE_BSWAP_64", DEFINES_FUNC, "HAVE_BYTESWAP_H", NULL,
+ "#include <byteswap.h>\n"
+ "static int func(int x) { return bswap_64(x); }" },
+ { "HAVE_BUILTIN_CHOOSE_EXPR", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_choose_expr(1, 0, \"garbage\");" },
+ { "HAVE_BUILTIN_CLZ", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_clz(1) == (sizeof(int)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CLZL", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_clzl(1) == (sizeof(long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CLZLL", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CTZ", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_ctz(1 << (sizeof(int)*8 - 1)) == (sizeof(int)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CTZL", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_ctzl(1UL << (sizeof(long)*8 - 1)) == (sizeof(long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CTZLL", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_ctzll(1ULL << (sizeof(long long)*8 - 1)) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CONSTANT_P", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_constant_p(1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_EXPECT", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_expect(argc == 1, 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_FFS", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_ffs(0) == 0 ? 0 : 1;" },
+ { "HAVE_BUILTIN_FFSL", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_ffsl(0L) == 0 ? 0 : 1;" },
+ { "HAVE_BUILTIN_FFSLL", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_ffsll(0LL) == 0 ? 0 : 1;" },
+ { "HAVE_BUILTIN_POPCOUNTL", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_popcountl(255L) == 8 ? 0 : 1;" },
+ { "HAVE_BUILTIN_TYPES_COMPATIBLE_P", INSIDE_MAIN, NULL, NULL,
+ "return __builtin_types_compatible_p(char *, int) ? 1 : 0;" },
+ { "HAVE_ICCARM_INTRINSICS", DEFINES_FUNC, NULL, NULL,
+ "#include <intrinsics.h>\n"
+ "int func(int v) {\n"
+ " return __CLZ(__RBIT(v));\n"
+ "}" },
+ { "HAVE_BYTESWAP_H", OUTSIDE_MAIN, NULL, NULL,
+ "#include <byteswap.h>\n" },
+ { "HAVE_CLOCK_GETTIME",
+ DEFINES_FUNC, "HAVE_STRUCT_TIMESPEC", NULL,
+ "#include <time.h>\n"
+ "static struct timespec func(void) {\n"
+ " struct timespec ts;\n"
+ " clock_gettime(CLOCK_REALTIME, &ts);\n"
+ " return ts;\n"
+ "}\n" },
+ { "HAVE_CLOCK_GETTIME_IN_LIBRT",
+ DEFINES_FUNC,
+ "HAVE_STRUCT_TIMESPEC !HAVE_CLOCK_GETTIME",
+ "-lrt",
+ "#include <time.h>\n"
+ "static struct timespec func(void) {\n"
+ " struct timespec ts;\n"
+ " clock_gettime(CLOCK_REALTIME, &ts);\n"
+ " return ts;\n"
+ "}\n",
+ /* This means HAVE_CLOCK_GETTIME, too */
+ "HAVE_CLOCK_GETTIME" },
+ { "HAVE_COMPOUND_LITERALS", INSIDE_MAIN, NULL, NULL,
+ "int *foo = (int[]) { 1, 2, 3, 4 };\n"
+ "return foo[0] ? 0 : 1;" },
+ { "HAVE_FCHDIR", DEFINES_EVERYTHING|EXECUTE, NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <sys/stat.h>\n"
+ "#include <fcntl.h>\n"
+ "#include <unistd.h>\n"
+ "int main(void) {\n"
+ " int fd = open(\"..\", O_RDONLY);\n"
+ " return fchdir(fd) == 0 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_ERR_H", DEFINES_FUNC, NULL, NULL,
+ "#include <err.h>\n"
+ "static void func(int arg) {\n"
+ " if (arg == 0)\n"
+ " err(1, \"err %u\", arg);\n"
+ " if (arg == 1)\n"
+ " errx(1, \"err %u\", arg);\n"
+ " if (arg == 3)\n"
+ " warn(\"warn %u\", arg);\n"
+ " if (arg == 4)\n"
+ " warnx(\"warn %u\", arg);\n"
+ "}\n" },
+ { "HAVE_FILE_OFFSET_BITS", DEFINES_EVERYTHING|EXECUTE,
+ "HAVE_32BIT_OFF_T", NULL,
+ "#define _FILE_OFFSET_BITS 64\n"
+ "#include <sys/types.h>\n"
+ "int main(int argc, char *argv[]) {\n"
+ " return sizeof(off_t) == 8 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_FOR_LOOP_DECLARATION", INSIDE_MAIN, NULL, NULL,
+ "for (int i = 0; i < argc; i++) { return 0; };\n"
+ "return 1;" },
+ { "HAVE_FLEXIBLE_ARRAY_MEMBER", OUTSIDE_MAIN, NULL, NULL,
+ "struct foo { unsigned int x; int arr[]; };" },
+ { "HAVE_GETPAGESIZE", DEFINES_FUNC, NULL, NULL,
+ "#include <unistd.h>\n"
+ "static int func(void) { return getpagesize(); }" },
+ { "HAVE_ISBLANK", DEFINES_FUNC, NULL, NULL,
+ "#define _GNU_SOURCE\n"
+ "#include <ctype.h>\n"
+ "static int func(void) { return isblank(' '); }" },
+ { "HAVE_LITTLE_ENDIAN", INSIDE_MAIN|EXECUTE, NULL, NULL,
+ "union { int i; char c[sizeof(int)]; } u;\n"
+ "u.i = 0x01020304;\n"
+ "return u.c[0] == 0x04 && u.c[1] == 0x03 && u.c[2] == 0x02 && u.c[3] == 0x01 ? 0 : 1;" },
+ { "HAVE_MEMMEM", DEFINES_FUNC, NULL, NULL,
+ "#define _GNU_SOURCE\n"
+ "#include <string.h>\n"
+ "static void *func(void *h, size_t hl, void *n, size_t nl) {\n"
+ "return memmem(h, hl, n, nl);"
+ "}\n", },
+ { "HAVE_MEMRCHR", DEFINES_FUNC, NULL, NULL,
+ "#define _GNU_SOURCE\n"
+ "#include <string.h>\n"
+ "static void *func(void *s, int c, size_t n) {\n"
+ "return memrchr(s, c, n);"
+ "}\n", },
+ { "HAVE_MMAP", DEFINES_FUNC, NULL, NULL,
+ "#include <sys/mman.h>\n"
+ "static void *func(int fd) {\n"
+ " return mmap(0, 65536, PROT_READ, MAP_SHARED, fd, 0);\n"
+ "}" },
+ { "HAVE_PROC_SELF_MAPS", DEFINES_EVERYTHING|EXECUTE, NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <sys/stat.h>\n"
+ "#include <fcntl.h>\n"
+ "int main(void) {\n"
+ " return open(\"/proc/self/maps\", O_RDONLY) != -1 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_QSORT_R_PRIVATE_LAST",
+ DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE, NULL, NULL,
+ "#define _GNU_SOURCE 1\n"
+ "#include <stdlib.h>\n"
+ "static int cmp(const void *lp, const void *rp, void *priv) {\n"
+ " *(unsigned int *)priv = 1;\n"
+ " return *(const int *)lp - *(const int *)rp; }\n"
+ "int main(void) {\n"
+ " int array[] = { 9, 2, 5 };\n"
+ " unsigned int called = 0;\n"
+ " qsort_r(array, 3, sizeof(int), cmp, &called);\n"
+ " return called && array[0] == 2 && array[1] == 5 && array[2] == 9 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_STRUCT_TIMESPEC",
+ DEFINES_FUNC, NULL, NULL,
+ "#include <time.h>\n"
+ "static void func(void) {\n"
+ " struct timespec ts;\n"
+ " ts.tv_sec = ts.tv_nsec = 1;\n"
+ "}\n" },
+ { "HAVE_SECTION_START_STOP",
+ DEFINES_FUNC, NULL, NULL,
+ "static void *__attribute__((__section__(\"mysec\"))) p = &p;\n"
+ "static int func(void) {\n"
+ " extern void *__start_mysec[], *__stop_mysec[];\n"
+ " return __stop_mysec - __start_mysec;\n"
+ "}\n" },
+ { "HAVE_STACK_GROWS_UPWARDS", DEFINES_EVERYTHING|EXECUTE, NULL, NULL,
+ "static long nest(const void *base, unsigned int i)\n"
+ "{\n"
+ " if (i == 0)\n"
+ " return (const char *)&i - (const char *)base;\n"
+ " return nest(base, i-1);\n"
+ "}\n"
+ "int main(int argc, char *argv[]) {\n"
+ " return (nest(&argc, argc) > 0) ? 0 : 1\n;"
+ "}\n" },
+ { "HAVE_STATEMENT_EXPR", INSIDE_MAIN, NULL, NULL,
+ "return ({ int x = argc; x == argc ? 0 : 1; });" },
+ { "HAVE_SYS_FILIO_H", OUTSIDE_MAIN, NULL, NULL, /* Solaris needs this for FIONREAD */
+ "#include <sys/filio.h>\n" },
+ { "HAVE_SYS_TERMIOS_H", OUTSIDE_MAIN, NULL, NULL,
+ "#include <sys/termios.h>\n" },
+ { "HAVE_TYPEOF", INSIDE_MAIN, NULL, NULL,
+ "__typeof__(argc) i; i = argc; return i == argc ? 0 : 1;" },
+ { "HAVE_UNALIGNED_ACCESS", DEFINES_EVERYTHING|EXECUTE, NULL, NULL,
+ "#include <string.h>\n"
+ "int main(int argc, char *argv[]) {\n"
+ " char pad[sizeof(int *) * 1];\n"
+ " strncpy(pad, argv[0], sizeof(pad));\n"
+ " return *(int *)(pad) == *(int *)(pad + 1);\n"
+ "}\n" },
+ { "HAVE_UTIME", DEFINES_FUNC, NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <utime.h>\n"
+ "static int func(const char *filename) {\n"
+ " struct utimbuf times = { 0 };\n"
+ " return utime(filename, &times);\n"
+ "}" },
+ { "HAVE_WARN_UNUSED_RESULT", DEFINES_FUNC, NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <utime.h>\n"
+ "static __attribute__((warn_unused_result)) int func(int i) {\n"
+ " return i + 1;\n"
+ "}" },
+};
+
+static char *grab_fd(int fd)
+{
+ int ret;
+ size_t max, size = 0;
+ char *buffer;
+
+ max = 16384;
+ buffer = malloc(max+1);
+ while ((ret = read(fd, buffer + size, max - size)) > 0) {
+ size += ret;
+ if (size == max)
+ buffer = realloc(buffer, max *= 2);
+ }
+ if (ret < 0)
+ err(1, "reading from command");
+ buffer[size] = '\0';
+ return buffer;
+}
+
+static char *run(const char *cmd, int *exitstatus)
+{
+ pid_t pid;
+ int p[2];
+ char *ret;
+ int status;
+
+ if (pipe(p) != 0)
+ err(1, "creating pipe");
+
+ pid = fork();
+ if (pid == -1)
+ err(1, "forking");
+
+ if (pid == 0) {
+ if (dup2(p[1], STDOUT_FILENO) != STDOUT_FILENO
+ || dup2(p[1], STDERR_FILENO) != STDERR_FILENO
+ || close(p[0]) != 0
+ || close(STDIN_FILENO) != 0
+ || open("/dev/null", O_RDONLY) != STDIN_FILENO)
+ exit(128);
+
+ status = system(cmd);
+ if (WIFEXITED(status))
+ exit(WEXITSTATUS(status));
+ /* Here's a hint... */
+ exit(128 + WTERMSIG(status));
+ }
+
+ close(p[1]);
+ ret = grab_fd(p[0]);
+ /* This shouldn't fail... */
+ if (waitpid(pid, &status, 0) != pid)
+ err(1, "Failed to wait for child");
+ close(p[0]);
+ if (WIFEXITED(status))
+ *exitstatus = WEXITSTATUS(status);
+ else
+ *exitstatus = -WTERMSIG(status);
+ return ret;
+}
+
+static char *connect_args(const char *argv[], const char *extra)
+{
+ unsigned int i, len = strlen(extra) + 1;
+ char *ret;
+
+ for (i = 1; argv[i]; i++)
+ len += 1 + strlen(argv[i]);
+
+ ret = malloc(len);
+ len = 0;
+ for (i = 1; argv[i]; i++) {
+ strcpy(ret + len, argv[i]);
+ len += strlen(argv[i]);
+ if (argv[i+1])
+ ret[len++] = ' ';
+ }
+ strcpy(ret + len, extra);
+ return ret;
+}
+
+static struct test *find_test(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
+ if (strcmp(tests[i].name, name) == 0)
+ return &tests[i];
+ }
+ abort();
+}
+
+#define PRE_BOILERPLATE "/* Test program generated by configurator. */\n"
+#define MAIN_START_BOILERPLATE "int main(int argc, char *argv[]) {\n"
+#define USE_FUNC_BOILERPLATE "(void)func;\n"
+#define MAIN_BODY_BOILERPLATE "return 0;\n"
+#define MAIN_END_BOILERPLATE "}\n"
+
+static bool run_test(const char *cmd, struct test *test)
+{
+ char *output;
+ FILE *outf;
+ int status;
+
+ if (test->done)
+ return test->answer;
+
+ if (test->depends) {
+ size_t len;
+ const char *deps = test->depends;
+ char *dep;
+
+ /* Space-separated dependencies, could be ! for inverse. */
+ while ((len = strcspn(deps, " "))) {
+ bool positive = true;
+ if (deps[len]) {
+ dep = strdup(deps);
+ dep[len] = '\0';
+ } else {
+ dep = (char *)deps;
+ }
+
+ if (dep[0] == '!') {
+ dep++;
+ positive = false;
+ }
+ if (run_test(cmd, find_test(dep)) != positive) {
+ test->answer = false;
+ test->done = true;
+ return test->answer;
+ }
+ deps += len;
+ deps += strspn(deps, " ");
+ }
+ }
+
+ outf = fopen(INPUT_FILE, "w");
+ if (!outf)
+ err(1, "creating %s", INPUT_FILE);
+
+ fprintf(outf, "%s", PRE_BOILERPLATE);
+ switch (test->style & ~(EXECUTE|MAY_NOT_COMPILE)) {
+ case INSIDE_MAIN:
+ fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+ fprintf(outf, "%s", test->fragment);
+ fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+ break;
+ case OUTSIDE_MAIN:
+ fprintf(outf, "%s", test->fragment);
+ fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_BODY_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+ break;
+ case DEFINES_FUNC:
+ fprintf(outf, "%s", test->fragment);
+ fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+ fprintf(outf, "%s", USE_FUNC_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_BODY_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+ break;
+ case DEFINES_EVERYTHING:
+ fprintf(outf, "%s", test->fragment);
+ break;
+ default:
+ abort();
+
+ }
+ fclose(outf);
+
+ if (verbose > 1)
+ if (system("cat " INPUT_FILE) == -1);
+
+ if (test->link) {
+ char *newcmd;
+ newcmd = malloc(strlen(cmd) + strlen(" ")
+ + strlen(test->link) + 1);
+ sprintf(newcmd, "%s %s", cmd, test->link);
+ if (verbose > 1)
+ printf("Extra link line: %s", newcmd);
+ cmd = newcmd;
+ }
+
+ output = run(cmd, &status);
+ if (status != 0 || strstr(output, "warning")) {
+ if (verbose)
+ printf("Compile %s for %s, status %i: %s\n",
+ status ? "fail" : "warning",
+ test->name, status, output);
+ if ((test->style & EXECUTE) && !(test->style & MAY_NOT_COMPILE))
+ errx(1, "Test for %s did not compile:\n%s",
+ test->name, output);
+ test->answer = false;
+ free(output);
+ } else {
+ /* Compile succeeded. */
+ free(output);
+ /* We run INSIDE_MAIN tests for sanity checking. */
+ if ((test->style & EXECUTE) || (test->style & INSIDE_MAIN)) {
+ output = run("./" OUTPUT_FILE, &status);
+ if (!(test->style & EXECUTE) && status != 0)
+ errx(1, "Test for %s failed with %i:\n%s",
+ test->name, status, output);
+ if (verbose && status)
+ printf("%s exited %i\n", test->name, status);
+ free(output);
+ }
+ test->answer = (status == 0);
+ }
+ test->done = true;
+
+ if (test->answer && test->overrides) {
+ struct test *override = find_test(test->overrides);
+ override->done = true;
+ override->answer = true;
+ }
+ return test->answer;
+}
+
+int main(int argc, const char *argv[])
+{
+ char *cmd;
+ unsigned int i;
+ const char *default_args[]
+ = { "", DEFAULT_COMPILER, DEFAULT_FLAGS, NULL };
+
+ if (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0) {
+ printf("Usage: configurator [-v] [<compiler> <flags>...]\n"
+ " <compiler> <flags> will have \"-o <outfile> <infile.c>\" appended\n"
+ "Default: %s %s\n",
+ DEFAULT_COMPILER, DEFAULT_FLAGS);
+ exit(0);
+ }
+ if (strcmp(argv[1], "-v") == 0) {
+ argc--;
+ argv++;
+ verbose = 1;
+ } else if (strcmp(argv[1], "-vv") == 0) {
+ argc--;
+ argv++;
+ verbose = 2;
+ }
+ }
+
+ if (argc == 1)
+ argv = default_args;
+
+ cmd = connect_args(argv, " -o " OUTPUT_FILE " " INPUT_FILE);
+ for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+ run_test(cmd, &tests[i]);
+
+ unlink(OUTPUT_FILE);
+ unlink(INPUT_FILE);
+
+ printf("/* Generated by CCAN configurator */\n"
+ "#ifndef CCAN_CONFIG_H\n"
+ "#define CCAN_CONFIG_H\n");
+ printf("#ifndef _GNU_SOURCE\n");
+ printf("#define _GNU_SOURCE /* Always use GNU extensions. */\n");
+ printf("#endif\n");
+ printf("#define CCAN_COMPILER \"%s\"\n", argv[1]);
+ printf("#define CCAN_CFLAGS \"%s\"\n\n", connect_args(argv+1, ""));
+ /* This one implies "#include <ccan/..." works, eg. for tdb2.h */
+ printf("#define HAVE_CCAN 1\n");
+ for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+ printf("#define %s %u\n", tests[i].name, tests[i].answer);
+ printf("#endif /* CCAN_CONFIG_H */\n");
+ return 0;
+}
diff --git a/core/ccan/compiler/LICENSE b/core/ccan/compiler/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/core/ccan/compiler/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/core/ccan/compiler/_info b/core/ccan/compiler/_info
new file mode 100644
index 0000000..d60dff4
--- /dev/null
+++ b/core/ccan/compiler/_info
@@ -0,0 +1,64 @@
+#include "config.h"
+#include <string.h>
+#include <stdio.h>
+
+/**
+ * compiler - macros for common compiler extensions
+ *
+ * Abstracts away some compiler hints. Currently these include:
+ * - COLD
+ * For functions not called in fast paths (aka. cold functions)
+ * - PRINTF_FMT
+ * For functions which take printf-style parameters.
+ * - CONST_FUNCTION
+ * For functions which return the same value for same parameters.
+ * - NEEDED
+ * For functions and variables which must be emitted even if unused.
+ * - UNNEEDED
+ * For functions and variables which need not be emitted if unused.
+ * - UNUSED
+ * For parameters which are not used.
+ * - IS_COMPILE_CONSTANT()
+ * For using different tradeoffs for compiletime vs runtime evaluation.
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ *
+ * Example:
+ * #include <ccan/compiler/compiler.h>
+ * #include <stdio.h>
+ * #include <stdarg.h>
+ *
+ * // Example of a (slow-path) logging function.
+ * static int log_threshold = 2;
+ * static void COLD PRINTF_FMT(2,3)
+ * logger(int level, const char *fmt, ...)
+ * {
+ * va_list ap;
+ * va_start(ap, fmt);
+ * if (level >= log_threshold)
+ * vfprintf(stderr, fmt, ap);
+ * va_end(ap);
+ * }
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * if (argc != 1) {
+ * logger(3, "Don't want %i arguments!\n", argc-1);
+ * return 1;
+ * }
+ * return 0;
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/core/ccan/compiler/compiler.h b/core/ccan/compiler/compiler.h
new file mode 100644
index 0000000..bce4f25
--- /dev/null
+++ b/core/ccan/compiler/compiler.h
@@ -0,0 +1,231 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_COMPILER_H
+#define CCAN_COMPILER_H
+#include "config.h"
+
+#ifndef COLD
+#if HAVE_ATTRIBUTE_COLD
+/**
+ * COLD - a function is unlikely to be called.
+ *
+ * Used to mark an unlikely code path and optimize appropriately.
+ * It is usually used on logging or error routines.
+ *
+ * Example:
+ * static void COLD moan(const char *reason)
+ * {
+ * fprintf(stderr, "Error: %s (%s)\n", reason, strerror(errno));
+ * }
+ */
+#define COLD __attribute__((__cold__))
+#else
+#define COLD
+#endif
+#endif
+
+#ifndef NORETURN
+#if HAVE_ATTRIBUTE_NORETURN
+/**
+ * NORETURN - a function does not return
+ *
+ * Used to mark a function which exits; useful for suppressing warnings.
+ *
+ * Example:
+ * static void NORETURN fail(const char *reason)
+ * {
+ * fprintf(stderr, "Error: %s (%s)\n", reason, strerror(errno));
+ * exit(1);
+ * }
+ */
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#endif
+#endif
+
+#ifndef PRINTF_FMT
+#if HAVE_ATTRIBUTE_PRINTF
+/**
+ * PRINTF_FMT - a function takes printf-style arguments
+ * @nfmt: the 1-based number of the function's format argument.
+ * @narg: the 1-based number of the function's first variable argument.
+ *
+ * This allows the compiler to check your parameters as it does for printf().
+ *
+ * Example:
+ * void PRINTF_FMT(2,3) my_printf(const char *prefix, const char *fmt, ...);
+ */
+#define PRINTF_FMT(nfmt, narg) \
+ __attribute__((format(__printf__, nfmt, narg)))
+#else
+#define PRINTF_FMT(nfmt, narg)
+#endif
+#endif
+
+#ifndef CONST_FUNCTION
+#if HAVE_ATTRIBUTE_CONST
+/**
+ * CONST_FUNCTION - a function's return depends only on its argument
+ *
+ * This allows the compiler to assume that the function will return the exact
+ * same value for the exact same arguments. This implies that the function
+ * must not use global variables, or dereference pointer arguments.
+ */
+#define CONST_FUNCTION __attribute__((__const__))
+#else
+#define CONST_FUNCTION
+#endif
+
+#ifndef PURE_FUNCTION
+#if HAVE_ATTRIBUTE_PURE
+/**
+ * PURE_FUNCTION - a function is pure
+ *
+ * A pure function is one that has no side effects other than it's return value
+ * and uses no inputs other than it's arguments and global variables.
+ */
+#define PURE_FUNCTION __attribute__((__pure__))
+#else
+#define PURE_FUNCTION
+#endif
+#endif
+#endif
+
+#if HAVE_ATTRIBUTE_UNUSED
+#ifndef UNNEEDED
+/**
+ * UNNEEDED - a variable/function may not be needed
+ *
+ * This suppresses warnings about unused variables or functions, but tells
+ * the compiler that if it is unused it need not emit it into the source code.
+ *
+ * Example:
+ * // With some preprocessor options, this is unnecessary.
+ * static UNNEEDED int counter;
+ *
+ * // With some preprocessor options, this is unnecessary.
+ * static UNNEEDED void add_to_counter(int add)
+ * {
+ * counter += add;
+ * }
+ */
+#define UNNEEDED __attribute__((__unused__))
+#endif
+
+#ifndef NEEDED
+#if HAVE_ATTRIBUTE_USED
+/**
+ * NEEDED - a variable/function is needed
+ *
+ * This suppresses warnings about unused variables or functions, but tells
+ * the compiler that it must exist even if it (seems) unused.
+ *
+ * Example:
+ * // Even if this is unused, these are vital for debugging.
+ * static NEEDED int counter;
+ * static NEEDED void dump_counter(void)
+ * {
+ * printf("Counter is %i\n", counter);
+ * }
+ */
+#define NEEDED __attribute__((__used__))
+#else
+/* Before used, unused functions and vars were always emitted. */
+#define NEEDED __attribute__((__unused__))
+#endif
+#endif
+
+#ifndef UNUSED
+/**
+ * UNUSED - a parameter is unused
+ *
+ * Some compilers (eg. gcc with -W or -Wunused) warn about unused
+ * function parameters. This suppresses such warnings and indicates
+ * to the reader that it's deliberate.
+ *
+ * Example:
+ * // This is used as a callback, so needs to have this prototype.
+ * static int some_callback(void *unused UNUSED)
+ * {
+ * return 0;
+ * }
+ */
+#define UNUSED __attribute__((__unused__))
+#endif
+#else
+#ifndef UNNEEDED
+#define UNNEEDED
+#endif
+#ifndef NEEDED
+#define NEEDED
+#endif
+#ifndef UNUSED
+#define UNUSED
+#endif
+#endif
+
+#ifndef IS_COMPILE_CONSTANT
+#if HAVE_BUILTIN_CONSTANT_P
+/**
+ * IS_COMPILE_CONSTANT - does the compiler know the value of this expression?
+ * @expr: the expression to evaluate
+ *
+ * When an expression manipulation is complicated, it is usually better to
+ * implement it in a function. However, if the expression being manipulated is
+ * known at compile time, it is better to have the compiler see the entire
+ * expression so it can simply substitute the result.
+ *
+ * This can be done using the IS_COMPILE_CONSTANT() macro.
+ *
+ * Example:
+ * enum greek { ALPHA, BETA, GAMMA, DELTA, EPSILON };
+ *
+ * // Out-of-line version.
+ * const char *greek_name(enum greek greek);
+ *
+ * // Inline version.
+ * static inline const char *_greek_name(enum greek greek)
+ * {
+ * switch (greek) {
+ * case ALPHA: return "alpha";
+ * case BETA: return "beta";
+ * case GAMMA: return "gamma";
+ * case DELTA: return "delta";
+ * case EPSILON: return "epsilon";
+ * default: return "**INVALID**";
+ * }
+ * }
+ *
+ * // Use inline if compiler knows answer. Otherwise call function
+ * // to avoid copies of the same code everywhere.
+ * #define greek_name(g) \
+ * (IS_COMPILE_CONSTANT(greek) ? _greek_name(g) : greek_name(g))
+ */
+#define IS_COMPILE_CONSTANT(expr) __builtin_constant_p(expr)
+#else
+/* If we don't know, assume it's not. */
+#define IS_COMPILE_CONSTANT(expr) 0
+#endif
+#endif
+
+#ifndef WARN_UNUSED_RESULT
+#if HAVE_WARN_UNUSED_RESULT
+/**
+ * WARN_UNUSED_RESULT - warn if a function return value is unused.
+ *
+ * Used to mark a function where it is extremely unlikely that the caller
+ * can ignore the result, eg realloc().
+ *
+ * Example:
+ * // buf param may be freed by this; need return value!
+ * static char *WARN_UNUSED_RESULT enlarge(char *buf, unsigned *size)
+ * {
+ * return realloc(buf, (*size) *= 2);
+ * }
+ */
+#define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+#endif
+#endif /* CCAN_COMPILER_H */
diff --git a/core/ccan/compiler/test/compile_fail-printf.c b/core/ccan/compiler/test/compile_fail-printf.c
new file mode 100644
index 0000000..8f34ae5
--- /dev/null
+++ b/core/ccan/compiler/test/compile_fail-printf.c
@@ -0,0 +1,22 @@
+#include <ccan/compiler/compiler.h>
+
+static void PRINTF_FMT(2,3) my_printf(int x, const char *fmt, ...)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i = 0;
+
+ my_printf(1, "Not a pointer "
+#ifdef FAIL
+ "%p",
+#if !HAVE_ATTRIBUTE_PRINTF
+#error "Unfortunately we don't fail if !HAVE_ATTRIBUTE_PRINTF."
+#endif
+#else
+ "%i",
+#endif
+ i);
+ return 0;
+}
diff --git a/core/ccan/compiler/test/run-is_compile_constant.c b/core/ccan/compiler/test/run-is_compile_constant.c
new file mode 100644
index 0000000..a66f2e1
--- /dev/null
+++ b/core/ccan/compiler/test/run-is_compile_constant.c
@@ -0,0 +1,15 @@
+#include <ccan/compiler/compiler.h>
+#include <ccan/tap/tap.h>
+
+int main(int argc, char *argv[])
+{
+ plan_tests(2);
+
+ ok1(!IS_COMPILE_CONSTANT(argc));
+#if HAVE_BUILTIN_CONSTANT_P
+ ok1(IS_COMPILE_CONSTANT(7));
+#else
+ pass("If !HAVE_BUILTIN_CONSTANT_P, IS_COMPILE_CONSTANT always false");
+#endif
+ return exit_status();
+}
diff --git a/core/ccan/tap/_info b/core/ccan/tap/_info
new file mode 100644
index 0000000..2b116de
--- /dev/null
+++ b/core/ccan/tap/_info
@@ -0,0 +1,61 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * tap - Test Anything Protocol
+ *
+ * The tap package produces simple-to-parse mainly-human-readable test
+ * output to assist in the writing of test cases. It is based on the
+ * (now-defunct) libtap, which is based on Perl's CPAN TAP module. Its
+ * output can be parsed by a harness such as CPAN's Prove.
+ *
+ * CCAN testcases are expected to output the TAP format, usually using
+ * this package.
+ *
+ * For more information about TAP, see:
+ * http://en.wikipedia.org/wiki/Test_Anything_Protocol
+ *
+ * Based on the original libtap, Copyright (c) 2004 Nik Clayton.
+ *
+ * License: BSD (2 clause)
+ *
+ * Example:
+ * #include <string.h>
+ * #include <ccan/tap/tap.h>
+ *
+ * // Run some simple (but overly chatty) tests on strcmp().
+ * int main(int argc, char *argv[])
+ * {
+ * const char a[] = "a", another_a[] = "a";
+ * const char b[] = "b";
+ * const char ab[] = "ab";
+ *
+ * plan_tests(4);
+ * diag("Testing different pointers (%p/%p) with same contents",
+ * a, another_a);
+ * ok1(strcmp(a, another_a) == 0);
+ *
+ * diag("'a' comes before 'b'");
+ * ok1(strcmp(a, b) < 0);
+ * ok1(strcmp(b, a) > 0);
+ *
+ * diag("'ab' comes after 'a'");
+ * ok1(strcmp(ab, a) > 0);
+ * return exit_status();
+ * }
+ *
+ * Maintainer: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/compiler\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/core/ccan/tap/tap.3 b/core/ccan/tap/tap.3
new file mode 100644
index 0000000..0abab74
--- /dev/null
+++ b/core/ccan/tap/tap.3
@@ -0,0 +1,362 @@
+.Dd December 20, 2004
+.Os
+.Dt TAP 3
+.Sh NAME
+.Nm tap
+.Nd write tests that implement the Test Anything Protocol
+.Sh SYNOPSIS
+.In tap.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides functions for writing test scripts that produce output
+consistent with the Test Anything Protocol. A test harness that parses
+this protocol can run these tests and produce useful reports indicating
+their success or failure.
+.Ss PRINTF STRINGS
+In the descriptions that follow, for any function that takes as the
+last two parameters
+.Dq Fa const char * , Fa ...
+it can be assumed that the
+.Fa const char *
+is a
+.Fn printf
+-like format string, and the optional arguments are values to be placed
+in that string.
+.Ss TEST PLANS
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn plan_tests "unsigned int"
+.Xc
+.It Xo
+.Ft void
+.Fn plan_no_plan "void"
+.Xc
+.It Xo
+.Ft void
+.Fn plan_skip_all "const char *" "..."
+.Xc
+.El
+.Pp
+You must first specify a test plan. This indicates how many tests you
+intend to run, and allows the test harness to notice if any tests were
+missed, or if the test program exited prematurely.
+.Pp
+To do this, use
+.Fn plan_tests .
+The function will cause your program to exit prematurely if you specify
+0 tests.
+.Pp
+In some situations you may not know how many tests you will be running, or
+you are developing your test program, and do not want to update the
+.Fn plan_tests
+parameter every time you make a change. For those situations use
+.Fn plan_no_plan .
+It indicates to the test harness that an indeterminate number
+of tests will be run.
+.Pp
+Both
+.Fn plan_tests
+and
+.Fn plan_no_plan
+will cause your test program to exit prematurely with a diagnostic
+message if they are called more than once.
+.Pp
+If your test program detects at run time that some required functionality
+is missing (for example, it relies on a database connection which is not
+present, or a particular configuration option that has not been included
+in the running kernel) use
+.Fn plan_skip_all ,
+passing as parameters a string to display indicating the reason for skipping
+the tests.
+.Ss SIMPLE TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft unsigned int
+.Fn ok "expression" "const char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn ok1 "expression"
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn pass "const char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn fail "const char *" "..."
+.Xc
+.El
+.Pp
+Tests are implemented as expressions checked by calls to the
+.Fn ok
+and
+.Fn ok1
+macros. In both cases
+.Fa expression
+should evaluate to true if the test succeeded.
+.Pp
+.Fn ok
+allows you to specify a name, or comment, describing the test which will
+be included in the output.
+.Fn ok1
+is for those times when the expression to be tested is self
+explanatory and does not need an associated comment. In those cases
+the test expression becomes the comment.
+.Pp
+These four calls are equivalent:
+.Bd -literal -offset indent
+int i = 5;
+
+ok(i == 5, "i equals 5"); /* Overly verbose */
+ok(i == 5, "i equals %d", i); /* Just to demonstrate printf-like
+ behaviour of the test name */
+ok(i == 5, "i == 5"); /* Needless repetition */
+ok1(i == 5); /* Just right */
+.Ed
+.Pp
+It is good practice to ensure that the test name describes the meaning
+behind the test rather than what you are testing. Viz
+.Bd -literal -offset indent
+ok(db != NULL, "db is not NULL"); /* Not bad, but */
+ok(db != NULL, "Database conn. succeeded"); /* this is better */
+.Ed
+.Pp
+.Fn ok
+and
+.Fn ok1
+return 1 if the expression evaluated to true, and 0 if it evaluated to
+false. This lets you chain calls from
+.Fn ok
+to
+.Fn diag
+to only produce diagnostic output if the test failed. For example, this
+code will include diagnostic information about why the database connection
+failed, but only if the test failed.
+.Bd -literal -offset indent
+if (!ok(db != NULL, "Database conn. succeeded")) {
+ diag("Database error code: %d", dberrno);
+}
+.Ed
+.Pp
+You also have
+.Fn pass
+and
+.Fn fail .
+From the Test::More documentation:
+.Bd -literal -offset indent
+Sometimes you just want to say that the tests have passed.
+Usually the case is you've got some complicated condition
+that is difficult to wedge into an ok(). In this case,
+you can simply use pass() (to declare the test ok) or fail
+(for not ok).
+
+Use these very, very, very sparingly.
+.Ed
+.Pp
+These are synonyms for ok(1, ...) and ok(0, ...).
+.Ss SKIPPING TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn skip "unsigned int" "const char *" "..."
+.Xc
+.It Xo
+.Fn skip_if "expression" "unsigned int" "const char *" "..."
+.Xc
+.El
+.Pp
+Sets of tests can be skipped. Ordinarily you would do this because
+the test can't be run in this particular testing environment.
+.Pp
+For example, suppose some tests should be run as root. If the test is
+not being run as root then the tests should be skipped. In this
+implementation, skipped tests are flagged as being ok, with a special
+message indicating that they were skipped. It is your responsibility
+to ensure that the number of tests skipped (the first parameter to
+.Fn skip )
+is correct for the number of tests to skip.
+.Pp
+One way of implementing this is with a
+.Dq do { } while(0);
+loop, or an
+.Dq if( ) { } else { }
+construct, to ensure that there are no additional side effects from the
+skipped tests.
+.Bd -literal -offset indent
+if(getuid() != 0) {
+ skip(1, "because test only works as root");
+} else {
+ ok(do_something_as_root() == 0, "Did something as root");
+}
+.Ed
+.Pp
+A convenient macro is provided to assist with this. The previous example could
+be re-written as follows.
+.Bd -literal -offset indent
+skip_if(getuid() != 0, 1, "because test only works as root") {
+ ok(do_something_as_root() == 0, "Did something as root");
+}
+.Ed
+.Ss MARKING TESTS AS Dq TODO
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn todo_start "const char *" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn todo_end "void"
+.Xc
+.El
+.Pp
+Sets of tests can be flagged as being
+.Dq TODO .
+These are tests that you expect to fail, probably because you haven't
+fixed a bug, or finished a new feature yet. These tests will still be
+run, but with additional output that indicates that they are expected
+to fail. Should a test start to succeed unexpectedly, tools like
+.Xr prove 1
+will indicate this, and you can move the test out of the todo
+block. This is much more useful than simply commenting out (or
+.Dq #ifdef 0 ... #endif )
+the tests.
+.Bd -literal -offset indent
+todo_start("dwim() not returning true yet");
+
+ok(dwim(), "Did what the user wanted");
+
+todo_end();
+.Ed
+.Pp
+Should
+.Fn dwim
+ever start succeeding you will know about it as soon as you run the
+tests. Note that
+.Em unlike
+the
+.Fn skip_*
+family, additional code between
+.Fn todo_start
+and
+.Fn todo_end
+.Em is
+executed.
+.Ss SKIP vs. TODO
+From the Test::More documentation;
+.Bd -literal -offset indent
+If it's something the user might not be able to do, use SKIP.
+This includes optional modules that aren't installed, running
+under an OS that doesn't have some feature (like fork() or
+symlinks), or maybe you need an Internet connection and one
+isn't available.
+
+If it's something the programmer hasn't done yet, use TODO.
+This is for any code you haven't written yet, or bugs you have
+yet to fix, but want to put tests in your testing script
+(always a good idea).
+.Ed
+.Ss DIAGNOSTIC OUTPUT
+.Bl -tag -width indent
+.It Xo
+.Fr int
+.Fn diag "const char *" "..."
+.Xc
+.El
+.Pp
+If your tests need to produce diagnostic output, use
+.Fn diag .
+It ensures that the output will not be considered by the TAP test harness.
+.Fn diag
+adds the necessary trailing
+.Dq \en
+for you.
+It returns the number of characters written.
+.Bd -literal -offset indent
+diag("Expected return code 0, got return code %d", rcode);
+.Ed
+.Ss EXIT STATUS
+.Bl -tag -width indent
+.It Xo
+.Fr int
+.Fn exit_status void
+.Xc
+.El
+.Pp
+For maximum compatability your test program should return a particular
+exit code. This is calculated by
+.Fn exit_status
+so it is sufficient to always return from
+.Fn main
+with either
+.Dq return exit_status();
+or
+.Dq exit(exit_status());
+as appropriate.
+.Sh EXAMPLES
+The
+.Pa tests
+directory in the source distribution contains numerous tests of
+.Nm
+functionality, written using
+.Nm .
+Examine them for examples of how to construct test suites.
+.Sh COMPATABILITY
+.Nm
+strives to be compatible with the Perl Test::More and Test::Harness
+modules. The test suite verifies that
+.Nm
+is bug-for-bug compatible with their behaviour. This is why some
+functions which would more naturally return nothing return constant
+values.
+.Pp
+If the
+.Lb libpthread
+is found at compile time,
+.Nm
+.Em should
+be thread safe. Indications to the contrary (and test cases that expose
+incorrect behaviour) are very welcome.
+.Sh SEE ALSO
+.Xr Test::More 1 ,
+.Xr Test::Harness 1 ,
+.Xr prove 1
+.Sh STANDARDS
+.Nm
+requires a
+.St -isoC-99
+compiler. Some of the
+.Nm
+functionality is implemented as variadic macros, and that functionality
+was not formally codified until C99. Patches to use
+.Nm
+with earlier compilers that have their own implementation of variadic
+macros will be gratefully received.
+.Sh HISTORY
+.Nm
+was written to help improve the quality and coverage of the FreeBSD
+regression test suite, and released in the hope that others find it
+a useful tool to help improve the quality of their code.
+.Sh AUTHORS
+.An "Nik Clayton" Aq nik@ngo.org.uk ,
+.Aq nik@FreeBSD.org
+.Pp
+.Nm
+would not exist without the efforts of
+.An "Michael G Schwern" Aq schqern@pobox.com ,
+.An "Andy Lester" Aq andy@petdance.com ,
+and the countless others who have worked on the Perl QA programme.
+.Sh BUGS
+Ideally, running the tests would have no side effects on the behaviour
+of the application you are testing. However, it is not always possible
+to avoid them. The following side effects of using
+.Nm
+are known.
+.Bl -bullet -offset indent
+.It
+stdout is set to unbuffered mode after calling any of the
+.Fn plan_*
+functions.
+.El
diff --git a/core/ccan/tap/tap.c b/core/ccan/tap/tap.c
new file mode 100644
index 0000000..bf8a276
--- /dev/null
+++ b/core/ccan/tap/tap.c
@@ -0,0 +1,457 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "config.h"
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "tap.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static const char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+static int test_pid;
+
+/* Encapsulate the pthread code in a conditional. In the absence of
+ libpthread the code does nothing.
+
+ If you have multiple threads calling ok() etc. at the same time you would
+ need this, but in that case your test numbers will be random and I'm not
+ sure it makes sense. --RR
+*/
+#ifdef WANT_PTHREAD
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M)
+# define UNLOCK pthread_mutex_unlock(&M)
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void
+_expected_tests(unsigned int tests)
+{
+
+ printf("1..%d\n", tests);
+ e_tests = tests;
+}
+
+static void
+diagv(const char *fmt, va_list ap)
+{
+ fputs("# ", stdout);
+ vfprintf(stdout, fmt, ap);
+ fputs("\n", stdout);
+}
+
+static void
+_diag(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ diagv(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, const char *file, unsigned int line,
+ const char *test_name, ...)
+{
+ va_list ap;
+ char *local_test_name = NULL;
+ char *c;
+ int name_is_digits;
+
+ LOCK;
+
+ test_count++;
+
+ /* Start by taking the test name and performing any printf()
+ expansions on it */
+ if(test_name != NULL) {
+ va_start(ap, test_name);
+ if (vasprintf(&local_test_name, test_name, ap) < 0)
+ local_test_name = NULL;
+ va_end(ap);
+
+ /* Make sure the test name contains more than digits
+ and spaces. Emit an error message and exit if it
+ does */
+ if(local_test_name) {
+ name_is_digits = 1;
+ for(c = local_test_name; *c != '\0'; c++) {
+ if(!isdigit((unsigned char)*c)
+ && !isspace((unsigned char)*c)) {
+ name_is_digits = 0;
+ break;
+ }
+ }
+
+ if(name_is_digits) {
+ _diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name);
+ _diag(" Very confusing.");
+ }
+ }
+ }
+
+ if(!ok) {
+ printf("not ");
+ failures++;
+ }
+
+ printf("ok %d", test_count);
+
+ if(test_name != NULL) {
+ printf(" - ");
+
+ /* Print the test name, escaping any '#' characters it
+ might contain */
+ if(local_test_name != NULL) {
+ flockfile(stdout);
+ for(c = local_test_name; *c != '\0'; c++) {
+ if(*c == '#')
+ fputc('\\', stdout);
+ fputc((int)*c, stdout);
+ }
+ funlockfile(stdout);
+ } else { /* vasprintf() failed, use a fixed message */
+ printf("%s", todo_msg_fixed);
+ }
+ }
+
+ /* If we're in a todo_start() block then flag the test as being
+ TODO. todo_msg should contain the message to print at this
+ point. If it's NULL then asprintf() failed, and we should
+ use the fixed message.
+
+ This is not counted as a failure, so decrement the counter if
+ the test failed. */
+ if(todo) {
+ printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+ if(!ok)
+ failures--;
+ }
+
+ printf("\n");
+
+ if(!ok)
+ _diag(" Failed %stest (%s:%s() at line %d)",
+ todo ? "(TODO) " : "", file, func, line);
+
+ free(local_test_name);
+
+ UNLOCK;
+
+ if (!ok && tap_fail_callback)
+ tap_fail_callback();
+
+ /* We only care (when testing) that ok is positive, but here we
+ specifically only want to return 1 or 0 */
+ return ok ? 1 : 0;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+static void
+_cleanup(void)
+{
+ /* If we forked, don't do cleanup in child! */
+ if (getpid() != test_pid)
+ return;
+
+ LOCK;
+
+ /* If plan_no_plan() wasn't called, and we don't have a plan,
+ and we're not skipping everything, then something happened
+ before we could produce any output */
+ if(!no_plan && !have_plan && !skip_all) {
+ _diag("Looks like your test died before it could output anything.");
+ UNLOCK;
+ return;
+ }
+
+ if(test_died) {
+ _diag("Looks like your test died just after %d.", test_count);
+ UNLOCK;
+ return;
+ }
+
+
+ /* No plan provided, but now we know how many tests were run, and can
+ print the header at the end */
+ if(!skip_all && (no_plan || !have_plan)) {
+ printf("1..%d\n", test_count);
+ }
+
+ if((have_plan && !no_plan) && e_tests < test_count) {
+ _diag("Looks like you planned %d tests but ran %d extra.",
+ e_tests, test_count - e_tests);
+ UNLOCK;
+ return;
+ }
+
+ if((have_plan || !no_plan) && e_tests > test_count) {
+ _diag("Looks like you planned %d tests but only ran %d.",
+ e_tests, test_count);
+ if(failures) {
+ _diag("Looks like you failed %d tests of %d run.",
+ failures, test_count);
+ }
+ UNLOCK;
+ return;
+ }
+
+ if(failures)
+ _diag("Looks like you failed %d tests of %d.",
+ failures, test_count);
+
+ UNLOCK;
+}
+
+/*
+ * Initialise the TAP library. Will only do so once, however many times it's
+ * called.
+ */
+static void
+_tap_init(void)
+{
+ static int run_once = 0;
+
+ if(!run_once) {
+ test_pid = getpid();
+ atexit(_cleanup);
+
+ /* stdout needs to be unbuffered so that the output appears
+ in the same place relative to stderr output as it does
+ with Test::Harness */
+// setbuf(stdout, 0);
+ run_once = 1;
+ }
+}
+
+/*
+ * Note that there's no plan.
+ */
+void
+plan_no_plan(void)
+{
+
+ LOCK;
+
+ _tap_init();
+
+ if(have_plan != 0) {
+ fprintf(stderr, "You tried to plan twice!\n");
+ test_died = 1;
+ UNLOCK;
+ exit(255);
+ }
+
+ have_plan = 1;
+ no_plan = 1;
+
+ UNLOCK;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+void
+plan_skip_all(const char *reason)
+{
+
+ LOCK;
+
+ _tap_init();
+
+ skip_all = 1;
+
+ printf("1..0");
+
+ if(reason != NULL)
+ printf(" # Skip %s", reason);
+
+ printf("\n");
+
+ UNLOCK;
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+void
+plan_tests(unsigned int tests)
+{
+
+ LOCK;
+
+ _tap_init();
+
+ if(have_plan != 0) {
+ fprintf(stderr, "You tried to plan twice!\n");
+ test_died = 1;
+ UNLOCK;
+ exit(255);
+ }
+
+ if(tests == 0) {
+ fprintf(stderr, "You said to run 0 tests! You've got to run something.\n");
+ test_died = 1;
+ UNLOCK;
+ exit(255);
+ }
+
+ have_plan = 1;
+
+ _expected_tests(tests);
+
+ UNLOCK;
+}
+
+void
+diag(const char *fmt, ...)
+{
+ va_list ap;
+
+ LOCK;
+
+ va_start(ap, fmt);
+ diagv(fmt, ap);
+ va_end(ap);
+
+ UNLOCK;
+}
+
+void
+skip(unsigned int n, const char *fmt, ...)
+{
+ va_list ap;
+ char *skip_msg;
+
+ LOCK;
+
+ va_start(ap, fmt);
+ if (vasprintf(&skip_msg, fmt, ap) < 0)
+ skip_msg = NULL;
+ va_end(ap);
+
+ while(n-- > 0) {
+ test_count++;
+ printf("ok %d # skip %s\n", test_count,
+ skip_msg != NULL ?
+ skip_msg : "libtap():malloc() failed");
+ }
+
+ free(skip_msg);
+
+ UNLOCK;
+}
+
+void
+todo_start(const char *fmt, ...)
+{
+ va_list ap;
+
+ LOCK;
+
+ va_start(ap, fmt);
+ if (vasprintf(&todo_msg, fmt, ap) < 0)
+ todo_msg = NULL;
+ va_end(ap);
+
+ todo = 1;
+
+ UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+ LOCK;
+
+ todo = 0;
+ free(todo_msg);
+
+ UNLOCK;
+}
+
+static int
+exit_status_(void)
+{
+ int r;
+
+ LOCK;
+
+ /* If there's no plan, just return the number of failures */
+ if(no_plan || !have_plan) {
+ UNLOCK;
+ return failures;
+ }
+
+ /* Ran too many tests? Return the number of tests that were run
+ that shouldn't have been */
+ if(e_tests < test_count) {
+ r = test_count - e_tests;
+ UNLOCK;
+ return r;
+ }
+
+ /* Return the number of tests that failed + the number of tests
+ that weren't run */
+ r = failures + e_tests - test_count;
+ UNLOCK;
+
+ return r;
+}
+
+int
+exit_status(void)
+{
+ int r = exit_status_();
+ if (r > 255)
+ r = 255;
+ return r;
+}
diff --git a/core/ccan/tap/tap.h b/core/ccan/tap/tap.h
new file mode 100644
index 0000000..5b21ff7
--- /dev/null
+++ b/core/ccan/tap/tap.h
@@ -0,0 +1,251 @@
+#ifndef CCAN_TAP_H
+#define CCAN_TAP_H
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <ccan/compiler/compiler.h>
+
+/**
+ * plan_tests - announce the number of tests you plan to run
+ * @tests: the number of tests
+ *
+ * This should be the first call in your test program: it allows tracing
+ * of failures which mean that not all tests are run.
+ *
+ * If you don't know how many tests will actually be run, assume all of them
+ * and use skip() if you don't actually run some tests.
+ *
+ * Example:
+ * plan_tests(13);
+ */
+void plan_tests(unsigned int tests);
+
+/**
+ * ok1 - Simple conditional test
+ * @e: the expression which we expect to be true.
+ *
+ * This is the simplest kind of test: if the expression is true, the
+ * test passes. The name of the test which is printed will simply be
+ * file name, line number, and the expression itself.
+ *
+ * Example:
+ * ok1(somefunc() == 1);
+ */
+# define ok1(e) ((e) ? \
+ _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+ _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+/**
+ * ok - Conditional test with a name
+ * @e: the expression which we expect to be true.
+ * @...: the printf-style name of the test.
+ *
+ * If the expression is true, the test passes. The name of the test will be
+ * the filename, line number, and the printf-style string. This can be clearer
+ * than simply the expression itself.
+ *
+ * Example:
+ * ok1(somefunc() == 1);
+ * ok(somefunc() == 0, "Second somefunc() should fail");
+ */
+# define ok(e, ...) ((e) ? \
+ _gen_result(1, __func__, __FILE__, __LINE__, \
+ __VA_ARGS__) : \
+ _gen_result(0, __func__, __FILE__, __LINE__, \
+ __VA_ARGS__))
+
+/**
+ * pass - Note that a test passed
+ * @...: the printf-style name of the test.
+ *
+ * For complicated code paths, it can be easiest to simply call pass() in one
+ * branch and fail() in another.
+ *
+ * Example:
+ * int x = somefunc();
+ * if (x > 0)
+ * pass("somefunc() returned a valid value");
+ * else
+ * fail("somefunc() returned an invalid value");
+ */
+# define pass(...) ok(1, __VA_ARGS__)
+
+/**
+ * fail - Note that a test failed
+ * @...: the printf-style name of the test.
+ *
+ * For complicated code paths, it can be easiest to simply call pass() in one
+ * branch and fail() in another.
+ */
+# define fail(...) ok(0, __VA_ARGS__)
+
+/* I don't find these to be useful. */
+# define skip_if(cond, n, ...) \
+ if (cond) skip((n), __VA_ARGS__); \
+ else
+
+# define skip_start(test, n, ...) \
+ do { \
+ if((test)) { \
+ skip(n, __VA_ARGS__); \
+ continue; \
+ }
+
+# define skip_end } while(0)
+
+unsigned int _gen_result(int, const char *, const char *, unsigned int,
+ const char *, ...) PRINTF_FMT(5, 6);
+
+/**
+ * diag - print a diagnostic message (use instead of printf/fprintf)
+ * @fmt: the format of the printf-style message
+ *
+ * diag ensures that the output will not be considered to be a test
+ * result by the TAP test harness. It will append '\n' for you.
+ *
+ * Example:
+ * diag("Now running complex tests");
+ */
+void diag(const char *fmt, ...) PRINTF_FMT(1, 2);
+
+/**
+ * skip - print a diagnostic message (use instead of printf/fprintf)
+ * @n: number of tests you're skipping.
+ * @fmt: the format of the reason you're skipping the tests.
+ *
+ * Sometimes tests cannot be run because the test system lacks some feature:
+ * you should explicitly document that you're skipping tests using skip().
+ *
+ * From the Test::More documentation:
+ * If it's something the user might not be able to do, use SKIP. This
+ * includes optional modules that aren't installed, running under an OS that
+ * doesn't have some feature (like fork() or symlinks), or maybe you need an
+ * Internet connection and one isn't available.
+ *
+ * Example:
+ * #ifdef HAVE_SOME_FEATURE
+ * ok1(somefunc());
+ * #else
+ * skip(1, "Don't have SOME_FEATURE");
+ * #endif
+ */
+void skip(unsigned int n, const char *fmt, ...) PRINTF_FMT(2, 3);
+
+/**
+ * todo_start - mark tests that you expect to fail.
+ * @fmt: the reason they currently fail.
+ *
+ * It's extremely useful to write tests before you implement the matching fix
+ * or features: surround these tests by todo_start()/todo_end(). These tests
+ * will still be run, but with additional output that indicates that they are
+ * expected to fail.
+ *
+ * This way, should a test start to succeed unexpectedly, tools like prove(1)
+ * will indicate this and you can move the test out of the todo block. This
+ * is much more useful than simply commenting out (or '#if 0') the tests.
+ *
+ * From the Test::More documentation:
+ * If it's something the programmer hasn't done yet, use TODO. This is for
+ * any code you haven't written yet, or bugs you have yet to fix, but want to
+ * put tests in your testing script (always a good idea).
+ *
+ * Example:
+ * static bool dwim(void)
+ * {
+ * return false; // NYI
+ * }
+ * ...
+ * todo_start("dwim() not returning true yet");
+ * ok(dwim(), "Did what the user wanted");
+ * todo_end();
+ */
+void todo_start(const char *fmt, ...) PRINTF_FMT(1, 2);
+
+/**
+ * todo_end - end of tests you expect to fail.
+ *
+ * See todo_start().
+ */
+void todo_end(void);
+
+/**
+ * exit_status - the value that main should return.
+ *
+ * For maximum compatibility your test program should return a particular exit
+ * code (ie. 0 if all tests were run, and every test which was expected to
+ * succeed succeeded).
+ *
+ * Example:
+ * exit(exit_status());
+ */
+int exit_status(void);
+
+/**
+ * plan_no_plan - I have no idea how many tests I'm going to run.
+ *
+ * In some situations you may not know how many tests you will be running, or
+ * you are developing your test program, and do not want to update the
+ * plan_tests() call every time you make a change. For those situations use
+ * plan_no_plan() instead of plan_tests(). It indicates to the test harness
+ * that an indeterminate number of tests will be run.
+ *
+ * Remember, if you fail to plan, you plan to fail.
+ *
+ * Example:
+ * plan_no_plan();
+ * while (random() % 2)
+ * ok1(somefunc());
+ * exit(exit_status());
+ */
+void plan_no_plan(void);
+
+/**
+ * plan_skip_all - Indicate that you will skip all tests.
+ * @reason: the string indicating why you can't run any tests.
+ *
+ * If your test program detects at run time that some required functionality
+ * is missing (for example, it relies on a database connection which is not
+ * present, or a particular configuration option that has not been included
+ * in the running kernel) use plan_skip_all() instead of plan_tests().
+ *
+ * Example:
+ * #ifndef HAVE_SOME_FEATURE
+ * plan_skip_all("Need SOME_FEATURE support");
+ * exit(exit_status());
+ * #else
+ * plan_tests(13);
+ * ...
+ * #endif
+ */
+void plan_skip_all(const char *reason);
+
+/**
+ * tap_fail_callback - function to call when we fail
+ *
+ * This can be used to ease debugging, or exit on the first failure.
+ */
+void (*tap_fail_callback)(void);
+
+#endif /* CCAN_TAP_H */
diff --git a/core/ccan/tap/test/run.c b/core/ccan/tap/test/run.c
new file mode 100644
index 0000000..fb039a8
--- /dev/null
+++ b/core/ccan/tap/test/run.c
@@ -0,0 +1,133 @@
+/* We use the fact that pipes have a buffer greater than the size of
+ * any output, and change stdout and stderr to use that.
+ *
+ * Since we don't use libtap for output, this looks like one big test. */
+#include <ccan/tap/tap.h>
+#include <ccan/tap/tap.c>
+#include <stdio.h>
+#include <limits.h>
+#include <err.h>
+#include <string.h>
+#include <stdbool.h>
+#include <fnmatch.h>
+
+
+
+/* We dup stderr to here. */
+static int stderrfd;
+
+/* write_all inlined here to avoid circular dependency. */
+static void write_all(int fd, const void *data, size_t size)
+{
+ while (size) {
+ ssize_t done;
+
+ done = write(fd, data, size);
+ if (done <= 0)
+ _exit(1);
+ data = (const char *)data + done;
+ size -= done;
+ }
+}
+
+/* Simple replacement for err() */
+static void failmsg(const char *fmt, ...)
+{
+ char buf[1024];
+ va_list ap;
+
+ /* Write into buffer. */
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ write_all(stderrfd, "# ", 2);
+ write_all(stderrfd, buf, strlen(buf));
+ write_all(stderrfd, "\n", 1);
+ _exit(1);
+}
+
+static void expect(int fd, const char *pattern)
+{
+ char buffer[PIPE_BUF+1];
+ int r;
+
+ r = read(fd, buffer, sizeof(buffer)-1);
+ if (r < 0)
+ failmsg("reading from pipe");
+ buffer[r] = '\0';
+
+ if (fnmatch(pattern, buffer, 0) != 0)
+ failmsg("Expected '%s' got '%s'", pattern, buffer);
+}
+
+int main(int argc, char *argv[])
+{
+ int p[2];
+ int stdoutfd;
+
+ setbuf(stdout, 0);
+ printf("1..1\n");
+ stderrfd = dup(STDERR_FILENO);
+ if (stderrfd < 0)
+ err(1, "dup of stderr failed");
+
+ stdoutfd = dup(STDOUT_FILENO);
+ if (stdoutfd < 0)
+ err(1, "dup of stdout failed");
+
+ if (pipe(p) != 0)
+ failmsg("pipe failed");
+
+ if (dup2(p[1], STDERR_FILENO) < 0 || dup2(p[1], STDOUT_FILENO) < 0)
+ failmsg("Duplicating file descriptor");
+
+ plan_tests(10);
+ expect(p[0], "1..10\n");
+
+ ok(1, "msg1");
+ expect(p[0], "ok 1 - msg1\n");
+
+ ok(0, "msg2");
+ expect(p[0], "not ok 2 - msg2\n"
+ "# Failed test (*test/run.c:main() at line 91)\n");
+
+ ok1(true);
+ expect(p[0], "ok 3 - true\n");
+
+ ok1(false);
+ expect(p[0], "not ok 4 - false\n"
+ "# Failed test (*test/run.c:main() at line 98)\n");
+
+ pass("passed");
+ expect(p[0], "ok 5 - passed\n");
+
+ fail("failed");
+ expect(p[0], "not ok 6 - failed\n"
+ "# Failed test (*test/run.c:main() at line 105)\n");
+
+ skip(2, "skipping %s", "test");
+ expect(p[0], "ok 7 # skip skipping test\n"
+ "ok 8 # skip skipping test\n");
+
+ todo_start("todo");
+ ok1(false);
+ expect(p[0], "not ok 9 - false # TODO todo\n"
+ "# Failed (TODO) test (*test/run.c:main() at line 114)\n");
+ ok1(true);
+ expect(p[0], "ok 10 - true # TODO todo\n");
+ todo_end();
+
+ if (exit_status() != 3)
+ failmsg("Expected exit status 3, not %i", exit_status());
+
+#if 0
+ /* Manually run the atexit command. */
+ _cleanup();
+ expect(p[0], "# Looks like you failed 2 tests of 9.\n");
+#endif
+
+ write_all(stdoutfd, "ok 1 - All passed\n",
+ strlen("ok 1 - All passed\n"));
+ exit(0);
+}
diff --git a/core/licenses/CC0 b/core/licenses/CC0
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/core/licenses/CC0
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/core/map.c b/core/map.c
new file mode 100644
index 0000000..5fdc2eb
--- /dev/null
+++ b/core/map.c
@@ -0,0 +1,106 @@
+#include <ccan/tap/tap.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include "map.h"
+
+static bool get(Map *map, const char *key, const void *data) {
+ return map_get(map, key) == data && map_closest(map, key) == data;
+}
+
+static bool compare(const char *key, void *value, void *data) {
+ Map *map = data;
+ ok(map_get(map, key) == value, "Compare map content");
+ return true;
+}
+
+static bool once(const char *key, void *value, void *data) {
+ int *counter = data;
+ (*counter)++;
+ return false;
+}
+
+static bool visit(const char *key, void *value, void *data) {
+ int *index = value;
+ int *visited = data;
+ visited[*index]++;
+ return true;
+}
+
+static int order_counter;
+
+static bool order(const char *key, void *value, void *data) {
+ int *index = value;
+ int *order = data;
+ order[*index] = ++order_counter;
+ return true;
+}
+
+int main(int argc, char *argv[]) {
+ const int values[3] = { 0, 1, 2 };
+
+ plan_no_plan();
+
+ Map *map = map_new();
+
+ ok(map && map_empty(map), "Creation");
+
+ ok(!map_get(map, "404"), "Get non-existing key");
+ ok(!map_contains(map, "404"), "Contains non-existing key");
+ ok(!map_closest(map, "404") && errno == ENOENT, "Closest non-existing key");
+
+ ok(!map_put(map, "a", NULL) && errno == EINVAL && map_empty(map) && !map_get(map, "a"), "Put NULL value");
+ ok(map_put(map, "a", &values[0]) && !map_empty(map) && get(map, "a", &values[0]), "Put 1");
+ ok(map_contains(map, "a"), "Contains existing key");
+ ok(map_closest(map, "a") == &values[0], "Closest match existing key");
+ ok(!map_put(map, "a", &values[1]) && errno == EEXIST && get(map, "a", &values[0]), "Put duplicate");
+ ok(map_put(map, "cafebabe", &values[2]) && get(map, "cafebabe", &values[2]), "Put 2");
+ ok(map_put(map, "cafe", &values[1]) && get(map, "cafe", &values[1]), "Put 3");
+
+ Map *copy = map_new();
+ ok(map_copy(copy, map), "Copy");
+ ok(!map_empty(copy), "Not empty after copying");
+ map_iterate(copy, compare, map);
+ map_iterate(map, compare, copy);
+
+ int counter = 0;
+ map_iterate(copy, once, &counter);
+ ok(counter == 1, "Iterate stop condition");
+
+ ok(!map_get(map, "ca") && !map_closest(map, "ca") && errno == 0, "Closest ambigious");
+
+ int visited[] = { 0, 0, 0 };
+
+ map_iterate(map, visit, &visited);
+ ok(visited[0] == 1 && visited[1] == 1 && visited[2] == 1, "Iterate map");
+
+ memset(visited, 0, sizeof visited);
+ order_counter = 0;
+ map_iterate(map, order, &visited);
+ ok(visited[0] == 1 && visited[1] == 2 && visited[2] == 3, "Ordered iteration");
+
+ memset(visited, 0, sizeof visited);
+ map_iterate(map_prefix(map, "ca"), visit, &visited);
+ ok(visited[0] == 0 && visited[1] == 1 && visited[2] == 1, "Iterate sub map");
+
+ memset(visited, 0, sizeof visited);
+ order_counter = 0;
+ map_iterate(map_prefix(map, "ca"), order, &visited);
+ ok(visited[0] == 0 && visited[1] == 1 && visited[2] == 2, "Ordered sub map iteration");
+
+ ok(map_empty(map_prefix(map, "404")), "Empty map for non-existing prefix");
+
+ ok(!map_delete(map, "404"), "Delete non-existing key");
+ ok(map_delete(map, "cafe") == &values[1] && !map_get(map, "cafe"), "Delete existing key");
+ ok(map_closest(map, "cafe") == &values[2], "Closest unambigious");
+
+ map_clear(map);
+ ok(map_empty(map), "Empty after clean");
+
+ map_free(map);
+ map_free(copy);
+
+ return exit_status();
+}
diff --git a/core/text.c b/core/text.c
new file mode 100644
index 0000000..3da647a
--- /dev/null
+++ b/core/text.c
@@ -0,0 +1,119 @@
+#include <ccan/tap/tap.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "text.h"
+#include "text-util.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+static bool insert(Text *txt, size_t pos, const char *data) {
+ return text_insert(txt, pos, data, strlen(data));
+}
+
+static bool isempty(Text *txt) {
+ return text_size(txt) == 0;
+}
+
+static char *content(Text *txt) {
+ static char buf[BUFSIZ];
+ size_t len = text_bytes_get(txt, 0, sizeof(buf)-1, buf);
+ buf[len] = '\0';
+ return buf;
+}
+
+static bool compare(Text *txt, const char *data) {
+ char buf[BUFSIZ];
+ size_t len = text_bytes_get(txt, 0, sizeof(buf)-1, buf);
+ buf[len] = '\0';
+ return len == strlen(data) && strcmp(buf, data) == 0;
+}
+
+int main(int argc, char *argv[]) {
+ Text *txt;
+
+ plan_no_plan();
+
+ txt = text_load("/");
+ ok(txt == NULL && errno == EISDIR, "Opening directory");
+
+ if (access("/etc/shadow", F_OK) == 0) {
+ txt = text_load("/etc/shadow");
+ ok(txt == NULL && errno == EACCES, "Opening file without sufficient permissions");
+ }
+
+ txt = text_load(NULL);
+ ok(txt != NULL && isempty(txt), "Opening empty file");
+
+ ok(insert(txt, 1, "") && isempty(txt), "Inserting empty data");
+ ok(!insert(txt, 1, " ") && isempty(txt), "Inserting with invalid offset");
+
+ /* test cached insertion (i.e. in-place with only one piece) */
+ ok(insert(txt, 0, "3") && compare(txt, "3"), "Inserting into empty document (cached)");
+ ok(insert(txt, 0, "1") && compare(txt, "13"), "Inserting at begin (cached)");
+ ok(insert(txt, 1, "2") && compare(txt, "123"), "Inserting in middle (cached)");
+ ok(insert(txt, text_size(txt), "4") && compare(txt, "1234"), "Inserting at end (cached)");
+
+ ok(text_delete(txt, text_size(txt), 0) && compare(txt, "1234"), "Deleting empty range");
+ ok(!text_delete(txt, text_size(txt), 1) && compare(txt, "1234"), "Deleting invalid offset");
+ ok(!text_delete(txt, 0, text_size(txt)+5) && compare(txt, "1234"), "Deleting invalid range");
+
+ ok(text_undo(txt) == 0 && compare(txt, ""), "Reverting to empty document");
+ ok(text_redo(txt) != EPOS /* == text_size(txt) */ && compare(txt, "1234"), "Restoring previsous content");
+
+ /* test cached deletion (i.e. in-place with only one piece) */
+ ok(text_delete(txt, text_size(txt)-1, 1) && compare(txt, "123"), "Deleting at end (cached)");
+ ok(text_delete(txt, 1, 1) && compare(txt, "13"), "Deleting in middle (cached)");
+ ok(text_delete(txt, 0, 1) && compare(txt, "3"), "Deleting at begin (cached)");
+ ok(text_delete(txt, 0, 1) && compare(txt, ""), "Deleting to empty document (cached)");
+
+ /* test regular insertion (i.e. with multiple pieces) */
+ text_snapshot(txt);
+ ok(insert(txt, 0, "3") && compare(txt, "3"), "Inserting into empty document");
+ text_snapshot(txt);
+ ok(insert(txt, 0, "1") && compare(txt, "13"), "Inserting at begin");
+ text_snapshot(txt);
+ ok(insert(txt, 1, "2") && compare(txt, "123"), "Inserting in between");
+ text_snapshot(txt);
+ ok(insert(txt, text_size(txt), "46") && compare(txt, "12346"), "Inserting at end");
+ text_snapshot(txt);
+ ok(insert(txt, 4, "5") && compare(txt, "123456"), "Inserting in middle");
+ text_snapshot(txt);
+ ok(insert(txt, text_size(txt), "789") && compare(txt, "123456789"), "Inserting at end");
+ text_snapshot(txt);
+ ok(insert(txt, text_size(txt), "0") && compare(txt, "1234567890"), "Inserting at end");
+
+ /* test simple undo / redo oparations */
+ ok(text_undo(txt) != EPOS && compare(txt, "123456789"), "Undo 1");
+ ok(text_undo(txt) != EPOS && compare(txt, "123456"), "Undo 2");
+ ok(text_undo(txt) != EPOS && compare(txt, "12346"), "Undo 3");
+ ok(text_undo(txt) != EPOS && compare(txt, "123"), "Undo 3");
+ ok(text_undo(txt) != EPOS && compare(txt, "13"), "Undo 5");
+ ok(text_undo(txt) != EPOS && compare(txt, "3"), "Undo 6");
+ ok(text_undo(txt) != EPOS && compare(txt, ""), "Undo 6");
+ ok(text_redo(txt) != EPOS && compare(txt, "3"), "Redo 1");
+ ok(text_redo(txt) != EPOS && compare(txt, "13"), "Redo 2");
+ ok(text_redo(txt) != EPOS && compare(txt, "123"), "Redo 3");
+ ok(text_redo(txt) != EPOS && compare(txt, "12346"), "Redo 4");
+ ok(text_redo(txt) != EPOS && compare(txt, "123456"), "Redo 5");
+ ok(text_redo(txt) != EPOS && compare(txt, "123456789"), "Redo 6");
+ ok(text_redo(txt) != EPOS && compare(txt, "1234567890"), "Redo 7");
+
+ /* test regular deletion (i.e. with multiple pieces) */
+ ok(text_delete(txt, 8, 2) && compare(txt, "12345678"), "Deleting midway start");
+ text_undo(txt);
+ ok(text_delete(txt, 2, 6) && compare(txt, "1290"), "Deleting midway end");
+ text_undo(txt);
+ ok(text_delete(txt, 7, 1) && compare(txt, "123456790"), "Deleting midway both same piece");
+ text_undo(txt);
+ ok(text_delete(txt, 0, 5) && compare(txt, "67890"), "Deleting at begin");
+ text_undo(txt);
+ ok(text_delete(txt, 5, 5) && compare(txt, "12345"), "Deleting at end");
+
+ return exit_status();
+}
diff --git a/util/.gitignore b/util/.gitignore
new file mode 100644
index 0000000..f8e3d19
--- /dev/null
+++ b/util/.gitignore
@@ -0,0 +1 @@
+/keys \ No newline at end of file
diff --git a/util/Makefile b/util/Makefile
new file mode 100644
index 0000000..cec7b21
--- /dev/null
+++ b/util/Makefile
@@ -0,0 +1,23 @@
+-include ../../config.mk
+
+DEPS_ROOT = ../../dependency/install
+DEPS_PREFIX = $(DEPS_ROOT)/usr
+DEPS_LIB = $(DEPS_PREFIX)/lib
+DEPS_INC = $(DEPS_PREFIX)/include
+
+CFLAGS_TERMKEY ?= $(shell pkg-config --cflags termkey || echo "-I/usr/local/include")
+LDFLAGS_TERMKEY ?= $(shell pkg-config --libs termkey || echo "-ltermkey")
+
+keys: keys.c
+ @echo Compiling keys utility
+ $(CC) $(CFLAGS_TERMKEY) keys.c $(LDFLAGS_TERMKEY) -o keys
+
+keys-local: keys.c
+ @echo Compiling keys utility locally
+ $(CC) -I$(DEPS_INC) keys.c -L$(DEPS_LIB) $(LDFLAGS_TERMKEY) $(LDFLAGS_CURSES) -o keys
+
+clean:
+ @echo cleaning
+ @rm -f keys
+
+.PHONY: clean keys-local
diff --git a/util/README.md b/util/README.md
new file mode 100644
index 0000000..3425336
--- /dev/null
+++ b/util/README.md
@@ -0,0 +1,11 @@
+Utility to turn symbolic keys into terminal input
+-------------------------------------------------
+
+This is a small helper utility which translates symbolic keys, as provided
+by [libtermkey](http://www.leonerd.org.uk/code/libtermkey/) and also used
+to specify key bindings within vis, to their corresponding codes understood
+by terminal programs.
+
+Type `make` to build the utility. `make keys-local` links the utility against
+a locally built version of libtermkey as produced by the top level `make local`
+Makefile target.
diff --git a/util/keys.c b/util/keys.c
new file mode 100644
index 0000000..4940400
--- /dev/null
+++ b/util/keys.c
@@ -0,0 +1,161 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termkey.h>
+
+static TermKey *termkey;
+
+static void die(const char *errstr, ...) {
+ va_list ap;
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+static void print(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+static void delay(void) {
+ usleep(termkey_get_waittime(termkey)*10000);
+}
+
+static void printkey(TermKeyKey *key) {
+ switch (key->type) {
+ case TERMKEY_TYPE_UNICODE:
+ if (key->modifiers & TERMKEY_KEYMOD_SHIFT)
+ ;
+ if (key->modifiers & TERMKEY_KEYMOD_CTRL)
+ key->utf8[0] &= 0x1f;
+ if (key->modifiers & TERMKEY_KEYMOD_ALT)
+ ;
+ print("%s", key->utf8);
+ break;
+ case TERMKEY_TYPE_FUNCTION:
+ break;
+ case TERMKEY_TYPE_KEYSYM:
+ switch (key->code.sym) {
+ case TERMKEY_SYM_UNKNOWN:
+ case TERMKEY_SYM_NONE:
+ die("Unknown key sym\n");
+ case TERMKEY_SYM_BACKSPACE:
+ print("\b");
+ break;
+ case TERMKEY_SYM_TAB:
+ if (key->modifiers & TERMKEY_KEYMOD_SHIFT)
+ print("\033[Z");
+ else
+ print("\t");
+ break;
+ case TERMKEY_SYM_ENTER:
+ print("\n");
+ break;
+ case TERMKEY_SYM_ESCAPE:
+ print("\033");
+ delay();
+ break;
+ case TERMKEY_SYM_SPACE:
+ print(" ");
+ break;
+ case TERMKEY_SYM_UP:
+ print("\033OA");
+ break;
+ case TERMKEY_SYM_DOWN:
+ print("\033OB");
+ break;
+ case TERMKEY_SYM_RIGHT:
+ print("\033OC");
+ break;
+ case TERMKEY_SYM_LEFT:
+ print("\033OD");
+ break;
+ case TERMKEY_SYM_DEL:
+ case TERMKEY_SYM_BEGIN:
+ case TERMKEY_SYM_FIND:
+ case TERMKEY_SYM_INSERT:
+ case TERMKEY_SYM_DELETE:
+ case TERMKEY_SYM_SELECT:
+ case TERMKEY_SYM_PAGEUP:
+ case TERMKEY_SYM_PAGEDOWN:
+ case TERMKEY_SYM_HOME:
+ case TERMKEY_SYM_END:
+ case TERMKEY_SYM_CANCEL:
+ case TERMKEY_SYM_CLEAR:
+ case TERMKEY_SYM_CLOSE:
+ case TERMKEY_SYM_COMMAND:
+ case TERMKEY_SYM_COPY:
+ case TERMKEY_SYM_EXIT:
+ case TERMKEY_SYM_HELP:
+ case TERMKEY_SYM_MARK:
+ case TERMKEY_SYM_MESSAGE:
+ case TERMKEY_SYM_MOVE:
+ case TERMKEY_SYM_OPEN:
+ case TERMKEY_SYM_OPTIONS:
+ case TERMKEY_SYM_PRINT:
+ case TERMKEY_SYM_REDO:
+ case TERMKEY_SYM_REFERENCE:
+ case TERMKEY_SYM_REFRESH:
+ case TERMKEY_SYM_REPLACE:
+ case TERMKEY_SYM_RESTART:
+ case TERMKEY_SYM_RESUME:
+ case TERMKEY_SYM_SAVE:
+ case TERMKEY_SYM_SUSPEND:
+ case TERMKEY_SYM_UNDO:
+ case TERMKEY_SYM_KP0:
+ case TERMKEY_SYM_KP1:
+ case TERMKEY_SYM_KP2:
+ case TERMKEY_SYM_KP3:
+ case TERMKEY_SYM_KP4:
+ case TERMKEY_SYM_KP5:
+ case TERMKEY_SYM_KP6:
+ case TERMKEY_SYM_KP7:
+ case TERMKEY_SYM_KP8:
+ case TERMKEY_SYM_KP9:
+ case TERMKEY_SYM_KPENTER:
+ case TERMKEY_SYM_KPPLUS:
+ case TERMKEY_SYM_KPMINUS:
+ case TERMKEY_SYM_KPMULT:
+ case TERMKEY_SYM_KPDIV:
+ case TERMKEY_SYM_KPCOMMA:
+ case TERMKEY_SYM_KPPERIOD:
+ case TERMKEY_SYM_KPEQUALS:
+ break;
+ }
+ break;
+ }
+}
+
+int main(int argc, char *argv[]) {
+ char buf[1024];
+ FILE *file = stdin;
+ char *term = getenv("TERM");
+ if (!term)
+ term = "xterm";
+ if (!(termkey = termkey_new_abstract(term, TERMKEY_FLAG_UTF8)))
+ die("Failed to initialize libtermkey\n");
+ while (fgets(buf, sizeof buf, file)) {
+ TermKeyKey key;
+ const char *keys = buf, *next;
+ while (*keys) {
+ if (*keys == '\n') {
+ keys++;
+ } else if (*keys == '<' && (next = termkey_strpkey(termkey, keys+1, &key, TERMKEY_FORMAT_VIM)) && *next == '>') {
+ printkey(&key);
+ keys = next+1;
+ } else if ((next = termkey_strpkey(termkey, keys, &key, TERMKEY_FORMAT_VIM))) {
+ printkey(&key);
+ keys = next;
+ } else {
+ die("Failed to parse keys: %s\n", keys);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/util/test b/util/test
new file mode 100644
index 0000000..7a99010
--- /dev/null
+++ b/util/test
@@ -0,0 +1 @@
+<S-Tab> \ No newline at end of file
diff --git a/vim/.gitignore b/vim/.gitignore
new file mode 100644
index 0000000..589a89a
--- /dev/null
+++ b/vim/.gitignore
@@ -0,0 +1,3 @@
+*.out
+*.err
+*.disabled \ No newline at end of file
diff --git a/vim/Makefile b/vim/Makefile
new file mode 100644
index 0000000..36a4ce7
--- /dev/null
+++ b/vim/Makefile
@@ -0,0 +1,15 @@
+test: ../../vis ../util/keys
+ @./test.sh
+
+../../vis: ../../*.[ch]
+ @echo Compiling vis
+ @$(MAKE) -C ../..
+
+../util/keys: ../util/keys.c
+ @$(MAKE) -C ../util
+
+clean:
+ @echo cleaning
+ @find . -name '*.out' -o -name '*.err' | xargs rm -f
+
+.PHONY: clean test
diff --git a/vim/README.md b/vim/README.md
new file mode 100644
index 0000000..1e42e8e
--- /dev/null
+++ b/vim/README.md
@@ -0,0 +1,16 @@
+Tests for vis - a vim-like editor frontend
+------------------------------------------
+
+The basic idea is to feed the same keyboard input to both vim
+and vis and compare their respective results.
+
+A test constitutes of 2 files:
+
+ * `test.in` the file/buffer content with which the editor is started
+ * `test.keys` a file containing the keyboard input as would normally
+ be typed by a user
+
+The toplevel shell script `test.sh` looks for these files in subdirectories,
+executes both editors and compares the resulting output.
+
+Type `make` to run all tests.
diff --git a/vim/golf/4d1a34ccfa85f32065000004/1.in b/vim/golf/4d1a34ccfa85f32065000004/1.in
new file mode 100644
index 0000000..2fd36cd
--- /dev/null
+++ b/vim/golf/4d1a34ccfa85f32065000004/1.in
@@ -0,0 +1,13 @@
+Make the pairs of lines match up by making each second line same as first:
+
+# Appending text:
+The name "Vim" is an acronym for "Vi IMproved"
+The name "Vim" is an acronym for
+
+# Editing text:
+Vim is a text editor originally released by Bram Moolenaar in 1991 for the Amiga
+Trivia: Vim is a text editor released by Bram Moolenaar in 1991 for the Amiga
+
+# Deleting text:
+Vim has a vi compatibility mode
+Vim has a vi compatibility mode but when not in this mode Vim has many enhancements over vi
diff --git a/vim/golf/4d1a34ccfa85f32065000004/1.keys b/vim/golf/4d1a34ccfa85f32065000004/1.keys
new file mode 100644
index 0000000..2dc4750
--- /dev/null
+++ b/vim/golf/4d1a34ccfa85f32065000004/1.keys
@@ -0,0 +1,6 @@
+qa /* start macro recording */
+/#<Enter> /* search for # */
+jyypjdd /* move down, yank, put, delete line below */
+q /* stop macro recording */
+@a /* replay macro */
+@@ /* replay again (TODO: support count for macro replay) */ \ No newline at end of file
diff --git a/vim/golf/4d1a34ccfa85f32065000004/1.ref b/vim/golf/4d1a34ccfa85f32065000004/1.ref
new file mode 100644
index 0000000..0fd29b0
--- /dev/null
+++ b/vim/golf/4d1a34ccfa85f32065000004/1.ref
@@ -0,0 +1,13 @@
+Make the pairs of lines match up by making each second line same as first:
+
+# Appending text:
+The name "Vim" is an acronym for "Vi IMproved"
+The name "Vim" is an acronym for "Vi IMproved"
+
+# Editing text:
+Vim is a text editor originally released by Bram Moolenaar in 1991 for the Amiga
+Vim is a text editor originally released by Bram Moolenaar in 1991 for the Amiga
+
+# Deleting text:
+Vim has a vi compatibility mode
+Vim has a vi compatibility mode
diff --git a/vim/golf/4d1ac1800a045132c0000011/1.in b/vim/golf/4d1ac1800a045132c0000011/1.in
new file mode 100644
index 0000000..f424d93
--- /dev/null
+++ b/vim/golf/4d1ac1800a045132c0000011/1.in
@@ -0,0 +1,2 @@
+#include<stdio.h>
+int main(void){puts("Hello world!");return 0;}
diff --git a/vim/golf/4d1ac1800a045132c0000011/1.keys b/vim/golf/4d1ac1800a045132c0000011/1.keys
new file mode 100644
index 0000000..992c6c4
--- /dev/null
+++ b/vim/golf/4d1ac1800a045132c0000011/1.keys
@@ -0,0 +1,9 @@
+f<i<Space><Escape> /* move to first < and insert space */
+o<Escape> /* insert new line */
+jf{i<Enter><Escape> /* insert new line before { */
+w. /* repeat to insert new line before puts */
+f;a<Enter><Escape> /* insert new line after ; */
+;. /* repeat last f search, repeat append */
+ki<Space><Escape> /* move line up, insert space */
+3. /* insert 3 more spaces (TODO: implement [count]i ?) */
+k^4. /* move line up, insert 4 spaces */ \ No newline at end of file
diff --git a/vim/golf/4d1ac1800a045132c0000011/1.ref b/vim/golf/4d1ac1800a045132c0000011/1.ref
new file mode 100644
index 0000000..41240ba
--- /dev/null
+++ b/vim/golf/4d1ac1800a045132c0000011/1.ref
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(void)
+{
+ puts("Hello world!");
+ return 0;
+}
diff --git a/vim/golf/4d665abd7d73e02a55000009/1.in b/vim/golf/4d665abd7d73e02a55000009/1.in
new file mode 100644
index 0000000..a381a49
--- /dev/null
+++ b/vim/golf/4d665abd7d73e02a55000009/1.in
@@ -0,0 +1,3 @@
+List<Double> c = new ArrayList<Double>();
+ 112.2), 102), 12), 954), 39.43), 49.4), 2224.6), 94), 123), 4929.55), 12), 98), 91.22)
+};
diff --git a/vim/golf/4d665abd7d73e02a55000009/1.keys.broken b/vim/golf/4d665abd7d73e02a55000009/1.keys.broken
new file mode 100644
index 0000000..81a68e9
--- /dev/null
+++ b/vim/golf/4d665abd7d73e02a55000009/1.keys.broken
@@ -0,0 +1,16 @@
+iList<<Escape> /* insert List< */
+~ /* upper case d to D */
+yiw /* yank Double */
+wx /* delete [ */
+r> /* overwrite ] with > */
+A new ArrayList<>();<Escape> /* append text */
+F< /* find backwards */
+"0p
+ /* paste from yank register */
+jdd /* delete { line */
+3J /* join data on single line */
+qa /* start macro recording */
+f,i)<Escape> /* insert ) before comma */
+q /* stop macro recording */
+@a /* replay macro */
+@@@@@@@@@@@@@@ /* again (TODO: count for macro replay, make @ repeatable) */
diff --git a/vim/golf/4d665abd7d73e02a55000009/1.ref b/vim/golf/4d665abd7d73e02a55000009/1.ref
new file mode 100644
index 0000000..796424a
--- /dev/null
+++ b/vim/golf/4d665abd7d73e02a55000009/1.ref
@@ -0,0 +1,14 @@
+List<Double> c = new ArrayList<Double>();
+c.add(112.2);
+c.add(102d);
+c.add(12d);
+c.add(954d);
+c.add(39.43);
+c.add(49.4);
+c.add(2224.6);
+c.add(94d);
+c.add(123d);
+c.add(4929.55);
+c.add(12d);
+c.add(98d);
+c.add(91.22);
diff --git a/vim/golf/4ddbd92898957e0001000016/1.in b/vim/golf/4ddbd92898957e0001000016/1.in
new file mode 100644
index 0000000..0be504e
--- /dev/null
+++ b/vim/golf/4ddbd92898957e0001000016/1.in
@@ -0,0 +1,24 @@
+China,1,2,3,4,5,6
+Brazil,3,1,2,5,5,6
+SAD,9,2,3,4,5,6
+UK,3,8,3,9,5,1
+France,7,2,3,4,5,6
+Germany,1,7,3,2,5,6
+Russia,1,6,2,9,5,6
+Spain,1,2,2,4,5,6
+Greece,1,3,3,4,5,6
+India,4,2,3,4,5,6
+Turkey,1,2,3,4,5,9
+Poland,1,2,3,4,5,6
+China,1,2,3,8,5,6
+Brazil,1,2,3,4,5,6
+SAD,1,2,3,4,5,6
+UK,1,2,3,4,5,6
+France,1,2,3,4,5,6
+Germany,1,2,3,4,5,6
+Russia,1,2,7,4,5,6
+Spain,1,2,3,4,5,6
+Greece,1,2,2,4,5,6
+India,1,2,8,4,5,6
+Turkey,1,2,1,7,5,6
+Poland,2,2,3,9,5,6
diff --git a/vim/golf/4ddbd92898957e0001000016/1.keys b/vim/golf/4ddbd92898957e0001000016/1.keys
new file mode 100644
index 0000000..d619478
--- /dev/null
+++ b/vim/golf/4ddbd92898957e0001000016/1.keys
@@ -0,0 +1,20 @@
+qq /* start macro recording */
+* /* search word under cursor */
+dd /* delete line */
+N /* repeat search */
+P /* put before */
+2j /* 2 lines down */
+q /* stop macro recording */
+@q /* 11@q repeat macro 11 times */
+@@
+@@
+@@
+@@
+@@
+@@
+@@
+@@
+@@
+@@
+@@
+@@
diff --git a/vim/golf/4ddbd92898957e0001000016/1.ref b/vim/golf/4ddbd92898957e0001000016/1.ref
new file mode 100644
index 0000000..8235b1e
--- /dev/null
+++ b/vim/golf/4ddbd92898957e0001000016/1.ref
@@ -0,0 +1,24 @@
+China,1,2,3,8,5,6
+China,1,2,3,4,5,6
+Brazil,1,2,3,4,5,6
+Brazil,3,1,2,5,5,6
+SAD,1,2,3,4,5,6
+SAD,9,2,3,4,5,6
+UK,1,2,3,4,5,6
+UK,3,8,3,9,5,1
+France,1,2,3,4,5,6
+France,7,2,3,4,5,6
+Germany,1,2,3,4,5,6
+Germany,1,7,3,2,5,6
+Russia,1,2,7,4,5,6
+Russia,1,6,2,9,5,6
+Spain,1,2,3,4,5,6
+Spain,1,2,2,4,5,6
+Greece,1,2,2,4,5,6
+Greece,1,3,3,4,5,6
+India,1,2,8,4,5,6
+India,4,2,3,4,5,6
+Turkey,1,2,1,7,5,6
+Turkey,1,2,3,4,5,9
+Poland,2,2,3,9,5,6
+Poland,1,2,3,4,5,6
diff --git a/vim/golf/4e31627b74ab580001000007/1.in b/vim/golf/4e31627b74ab580001000007/1.in
new file mode 100644
index 0000000..469ca12
--- /dev/null
+++ b/vim/golf/4e31627b74ab580001000007/1.in
@@ -0,0 +1,28 @@
+<?php
+class Foo
+{
+ private $var1;
+ private $var2;
+
+ public function Foo($val)
+ {
+ $this->init($val);
+ $this->doSomething();
+ }
+
+ private function init($val)
+ {
+ $this->var1 = $val;
+ }
+
+ private function doSomething()
+ {
+ $this->var2 = sqrt($this->var1);
+ }
+
+ public function getResult()
+ {
+ return $this->var2;
+ }
+}
+?>
diff --git a/vim/golf/4e31627b74ab580001000007/1.keys.broken b/vim/golf/4e31627b74ab580001000007/1.keys.broken
new file mode 100644
index 0000000..cb8ab02
--- /dev/null
+++ b/vim/golf/4e31627b74ab580001000007/1.keys.broken
@@ -0,0 +1,21 @@
+dd
+ipublic <Escape>
+:%s,\$this->,,g<Enter> /* we use , as delimiter because // interferes with the CPP */
+:%s,\$,,g<Enter>
+:%s,private var,private double var,<Enter>
+/sqrt<Enter>
+iMath.<Escape>
+gg
+/function<Enter>
+dw
+n
+cwvoid<Escape>
+n
+.
+n
+cwdouble<Escape>
+gg
+/val
+idouble <Escape>
+2n.
+/><Enter>dd /* TODO: Gdd does not work */ \ No newline at end of file
diff --git a/vim/golf/4e31627b74ab580001000007/1.ref b/vim/golf/4e31627b74ab580001000007/1.ref
new file mode 100644
index 0000000..b5415ed
--- /dev/null
+++ b/vim/golf/4e31627b74ab580001000007/1.ref
@@ -0,0 +1,26 @@
+public class Foo
+{
+ private double var1;
+ private double var2;
+
+ public Foo(double val)
+ {
+ init(val);
+ doSomething();
+ }
+
+ private void init(double val)
+ {
+ var1 = val;
+ }
+
+ private void doSomething()
+ {
+ var2 = Math.sqrt(var1);
+ }
+
+ public double getResult()
+ {
+ return var2;
+ }
+}
diff --git a/vim/golf/4ef209ef78702b0001000019/1.in b/vim/golf/4ef209ef78702b0001000019/1.in
new file mode 100644
index 0000000..ffe403c
--- /dev/null
+++ b/vim/golf/4ef209ef78702b0001000019/1.in
@@ -0,0 +1,19 @@
+#Set the global prefix key to C-q (default is C-b)
+set-option -g prefix C-q
+bind-key C-q last-window
+# Remove default binding since we’re replacing
+unbind %
+bind | split-window -h
+bind - split-window -v
+# Set status bar
+set -g status-bg black
+set -g status-fg white
+set -g status-left '#[fg=green]#H'
+# Highlight active window
+set-window-option -g window-status-current-bg red
+set -g status-right '#[fg=yellow]#(uptime | cut -d "," -f 2-)'
+# Set window notifications
+setw -g monitor-activity on
+set -g visual-activity on
+# Automatically set window title
+setw -g automatic-rename
diff --git a/vim/golf/4ef209ef78702b0001000019/1.keys b/vim/golf/4ef209ef78702b0001000019/1.keys
new file mode 100644
index 0000000..ff9234f
--- /dev/null
+++ b/vim/golf/4ef209ef78702b0001000019/1.keys
@@ -0,0 +1,6 @@
+/# <Enter>
+O<Enter><Escape>
+2n.
+2n.
+2n.
+2n.
diff --git a/vim/golf/4ef209ef78702b0001000019/1.ref b/vim/golf/4ef209ef78702b0001000019/1.ref
new file mode 100644
index 0000000..4dd2c3f
--- /dev/null
+++ b/vim/golf/4ef209ef78702b0001000019/1.ref
@@ -0,0 +1,29 @@
+#Set the global prefix key to C-q (default is C-b)
+set-option -g prefix C-q
+bind-key C-q last-window
+
+
+# Remove default binding since we’re replacing
+unbind %
+bind | split-window -h
+bind - split-window -v
+
+
+# Set status bar
+set -g status-bg black
+set -g status-fg white
+set -g status-left '#[fg=green]#H'
+
+
+# Highlight active window
+set-window-option -g window-status-current-bg red
+set -g status-right '#[fg=yellow]#(uptime | cut -d "," -f 2-)'
+
+
+# Set window notifications
+setw -g monitor-activity on
+set -g visual-activity on
+
+
+# Automatically set window title
+setw -g automatic-rename
diff --git a/vim/golf/4fbf8e303be58b0001000024/1.in b/vim/golf/4fbf8e303be58b0001000024/1.in
new file mode 100644
index 0000000..3ce3587
--- /dev/null
+++ b/vim/golf/4fbf8e303be58b0001000024/1.in
@@ -0,0 +1 @@
+{Vertex('x'): {Vertex('v'): Edge(Vertex('v'), Vertex('x')), Vertex('z'): Edge(Vertex('z'), Vertex('x')), Vertex('y'): Edge(Vertex('y'), Vertex('x'))}, Vertex('y'): {Vertex('x'): Edge(Vertex('y'), Vertex('x')), Vertex('w'): Edge(Vertex('w'), Vertex('y')), Vertex('z'): Edge(Vertex('z'), Vertex('y'))}, Vertex('z'): {Vertex('x'): Edge(Vertex('z'), Vertex('x')), Vertex('y'): Edge(Vertex('z'), Vertex('y')), Vertex('u'): Edge(Vertex('u'), Vertex('z'))}, Vertex('u'): {Vertex('v'): Edge(Vertex('v'), Vertex('u')), Vertex('w'): Edge(Vertex('w'), Vertex('u')), Vertex('z'): Edge(Vertex('u'), Vertex('z'))}, Vertex('v'): {Vertex('u'): Edge(Vertex('v'), Vertex('u')), Vertex('x'): Edge(Vertex('v'), Vertex('x')), Vertex('w'): Edge(Vertex('w'), Vertex('v'))}, Vertex('w'): {Vertex('u'): Edge(Vertex('w'), Vertex('u')), Vertex('v'): Edge(Vertex('w'), Vertex('v')), Vertex('y'): Edge(Vertex('w'), Vertex('y'))}}
diff --git a/vim/golf/4fbf8e303be58b0001000024/1.keys.broken b/vim/golf/4fbf8e303be58b0001000024/1.keys.broken
new file mode 100644
index 0000000..33ea81d
--- /dev/null
+++ b/vim/golf/4fbf8e303be58b0001000024/1.keys.broken
@@ -0,0 +1,17 @@
+a<Enter><Space><Space><Escape> /* line break after {, indent following line with 2 spaces */
+qq /* start macro recording */
+f{a<Enter><Space><Space><Space><Space><Escape> /* line break after {, indent following line with 4 spaces */
+2f,l.
+2;l.
+t}a<Enter><Space><Space><Escape>
+e.x
+q
+@q
+@@
+@@
+@@
+@@
+gg /* go to very first { of file */
+% /* go to matching bracket at end of file */
+i<Enter><Escape> /* move it to a new line */
+jdd /* delete last line of file */
diff --git a/vim/golf/4fbf8e303be58b0001000024/1.ref b/vim/golf/4fbf8e303be58b0001000024/1.ref
new file mode 100644
index 0000000..37bcf14
--- /dev/null
+++ b/vim/golf/4fbf8e303be58b0001000024/1.ref
@@ -0,0 +1,32 @@
+{
+ Vertex('x'): {
+ Vertex('v'): Edge(Vertex('v'), Vertex('x')),
+ Vertex('z'): Edge(Vertex('z'), Vertex('x')),
+ Vertex('y'): Edge(Vertex('y'), Vertex('x'))
+ },
+ Vertex('y'): {
+ Vertex('x'): Edge(Vertex('y'), Vertex('x')),
+ Vertex('w'): Edge(Vertex('w'), Vertex('y')),
+ Vertex('z'): Edge(Vertex('z'), Vertex('y'))
+ },
+ Vertex('z'): {
+ Vertex('x'): Edge(Vertex('z'), Vertex('x')),
+ Vertex('y'): Edge(Vertex('z'), Vertex('y')),
+ Vertex('u'): Edge(Vertex('u'), Vertex('z'))
+ },
+ Vertex('u'): {
+ Vertex('v'): Edge(Vertex('v'), Vertex('u')),
+ Vertex('w'): Edge(Vertex('w'), Vertex('u')),
+ Vertex('z'): Edge(Vertex('u'), Vertex('z'))
+ },
+ Vertex('v'): {
+ Vertex('u'): Edge(Vertex('v'), Vertex('u')),
+ Vertex('x'): Edge(Vertex('v'), Vertex('x')),
+ Vertex('w'): Edge(Vertex('w'), Vertex('v'))
+ },
+ Vertex('w'): {
+ Vertex('u'): Edge(Vertex('w'), Vertex('u')),
+ Vertex('v'): Edge(Vertex('w'), Vertex('v')),
+ Vertex('y'): Edge(Vertex('w'), Vertex('y'))
+ }
+}
diff --git a/vim/golf/50ad2cb165b8db0002000029/1.in b/vim/golf/50ad2cb165b8db0002000029/1.in
new file mode 100644
index 0000000..73c092d
--- /dev/null
+++ b/vim/golf/50ad2cb165b8db0002000029/1.in
@@ -0,0 +1,18 @@
+> "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
+> doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
+> veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim
+> ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
+incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
+nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+> Sed quia consequuntur magni dolores eos qui ratione voluptatem sequi
+> nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet,
+> consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt
+> ut labore et dolore magnam aliquam quaerat voluptatem.
+
+Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit
+laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur,
+vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"
diff --git a/vim/golf/50ad2cb165b8db0002000029/1.keys.broken b/vim/golf/50ad2cb165b8db0002000029/1.keys.broken
new file mode 100644
index 0000000..2697a0b
--- /dev/null
+++ b/vim/golf/50ad2cb165b8db0002000029/1.keys.broken
@@ -0,0 +1,12 @@
+/><Enter>
+dw
+n.
+n.
+2n.
+n.
+n.
+gg
+4J
+}j3J
+}j4J
+}j.
diff --git a/vim/golf/50ad2cb165b8db0002000029/1.ref b/vim/golf/50ad2cb165b8db0002000029/1.ref
new file mode 100644
index 0000000..7b76e5a
--- /dev/null
+++ b/vim/golf/50ad2cb165b8db0002000029/1.ref
@@ -0,0 +1,7 @@
+> "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+> Sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
+
+Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"
diff --git a/vim/golf/50ae009b65b8db0002000047/1.in b/vim/golf/50ae009b65b8db0002000047/1.in
new file mode 100644
index 0000000..593e03f
--- /dev/null
+++ b/vim/golf/50ae009b65b8db0002000047/1.in
@@ -0,0 +1,4 @@
+Ivm is an awesome text editor based on
+Iv, and is used to play a game called
+Ivmgolf. A challenge, simple for many
+Ivmgolfers, can still hide secrets.
diff --git a/vim/golf/50ae009b65b8db0002000047/1.keys b/vim/golf/50ae009b65b8db0002000047/1.keys
new file mode 100644
index 0000000..6d8c6d6
--- /dev/null
+++ b/vim/golf/50ae009b65b8db0002000047/1.keys
@@ -0,0 +1,7 @@
+qq
+g~2l
+xp
+q
++@q
++@@
++@@ \ No newline at end of file
diff --git a/vim/golf/50ae009b65b8db0002000047/1.ref b/vim/golf/50ae009b65b8db0002000047/1.ref
new file mode 100644
index 0000000..c03d61f
--- /dev/null
+++ b/vim/golf/50ae009b65b8db0002000047/1.ref
@@ -0,0 +1,4 @@
+Vim is an awesome text editor based on
+Vi, and is used to play a game called
+Vimgolf. A challenge, simple for many
+Vimgolfers, can still hide secrets.
diff --git a/vim/golf/50b1d7239aad89000200002d/1.in b/vim/golf/50b1d7239aad89000200002d/1.in
new file mode 100644
index 0000000..27c1bf2
--- /dev/null
+++ b/vim/golf/50b1d7239aad89000200002d/1.in
@@ -0,0 +1,10 @@
+<list>
+ <item>txt123</item>
+ <!-- <item>txt124</item> -->
+ <!-- <item>txt126</item> -->
+ <item>txt127</item>
+ <item>txt137</item>
+ <!-- <item>txt145</item> -->
+ <item>txt148</item>
+ <!-- <item>txt150</item> -->
+</list>
diff --git a/vim/golf/50b1d7239aad89000200002d/1.ref b/vim/golf/50b1d7239aad89000200002d/1.ref
new file mode 100644
index 0000000..652564f
--- /dev/null
+++ b/vim/golf/50b1d7239aad89000200002d/1.ref
@@ -0,0 +1 @@
+^(txt123|txt127|txt137|txt148).*$
diff --git a/vim/golf/50c13afab855760002000049/1.in b/vim/golf/50c13afab855760002000049/1.in
new file mode 100644
index 0000000..bcab9ea
--- /dev/null
+++ b/vim/golf/50c13afab855760002000049/1.in
@@ -0,0 +1,24 @@
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
diff --git a/vim/golf/50c13afab855760002000049/1.ref b/vim/golf/50c13afab855760002000049/1.ref
new file mode 100644
index 0000000..f466e20
--- /dev/null
+++ b/vim/golf/50c13afab855760002000049/1.ref
@@ -0,0 +1,24 @@
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
diff --git a/vim/golf/50c18a08b855760002000056/1.in b/vim/golf/50c18a08b855760002000056/1.in
new file mode 100644
index 0000000..9e71395
--- /dev/null
+++ b/vim/golf/50c18a08b855760002000056/1.in
@@ -0,0 +1,162 @@
+Left Hand Right Hand Alternating
+desegregated homophony dismantlement
+abracadabra nonillion authenticity
+aftereffect polyphony enchantment
+desegregate homonymy entitlement
+exaggerated Honolulu proficiency
+extraverted lollipop aneurysmal
+resegregate lollypop antisocial
+reverberate Monopoly Dickensian
+abstracted monopoly Kamehameha
+abstracter nonunion producible
+aftertaste Philippi proficient
+afterwards Pilipino Shenandoah
+aggravated polonium skepticism
+Artaxerxes holmium subproblem
+asseverate homonym suspensory
+Basseterre jillion Aleutians
+Bradstreet jollily amendment
+desecrater jumpily antitoxic
+devastated killjoy antivirus
+effervesce kinkily auditoria
+exacerbate Limpopo authentic
+exaggerate lumpily cockfight
+segregated million dirigible
+stagecraft minimum dismantle
+statecraft mullion disorient
+stewardess nonoily divisible
+sweetbread opinion Dixieland
+vertebrate Phillip dizygotic
+watercraft pillion downright
+watercress pumpkin dyspepsia
+abscessed hiphop dyspeptic
+addressee holily endowment
+aftercare homily firmament
+afterward hominy handiwork
+aggravate hookup Malayalam
+aggregate johnny Malaysian
+arrearage Joplin Maldivian
+attracted Kikuyu Mauritian
+barefaced kimono orificial
+beefeater Kunlun pantheism
+castrater limply protozoan
+cerebrate linkup provisory
+crabgrass minion rhapsodic
+Descartes mukluk sudorific
+desecrate muumuu Theodoric
+devastate Nippon tormentor
+eastwards oilily Uruguayan
+eggbeater Philip Vientiane
+extravert phyllo Aleutian
+federated phylum aneurism
+gazetteer pimply aneurysm
+readdress pinion antibody
+rearwards Pinyin Antiguan
+redbreast pinyin auditory
+redresser pinyon blandish
+regretter pippin bodywork
+Sacagawea plummy Boeotian
+sassafras pokily Buchanan
+scattered pompom Burnsian
+scatterer pompon busybody
+segregate poplin busywork
+staggered pullup chairman
+staggerer punily chrismal
+stargazer punkin clansman
+steadfast unholy clemency
+streetcar unhook corpsman
+traverser unlink cyclamen
+Tsvetaeva uphill dormancy
+vertebrae hilly downtick
+Watergate hippo downtown
+westwards hippy downturn
+Zacatecas hokku fiendish
+abstract hokum fishbowl
+acerbate Holly flamenco
+affected holly Futurism
+arrester honky futurism
+artefact hooky futurity
+asserter hulky Ghanaian
+attester hullo handheld
+baccarat hunky handmaid
+Barabbas Ilion Langland
+barrette Ilium Mahayana
+barterer ilium maieutic
+battered imply Malaysia
+batterer Jilin mandible
+bedstead Jimmy Menelaus
+begetter jimmy Nanchang
+bereaved jinni neighbor
+beverage jolly nepenthe
+braggart jumpy neuritic
+Caesarea junky neuritis
+carefree khoum neurosis
+caresser kinky neurotic
+Carreras knoll ornament
+Cascades kooky penchant
+cassette Lippi prorogue
+castrate lippy prosodic
+cataract Lonny protozoa
+crevasse loony prurient
+database loopy quantify
+deadbeat lumpy quantity
+deceased lupin rhapsody
+decrease lymph rifleman
+defeater milky Rightism
+defecate minim rightism
+deferrer Molly rigidity
+degraded molly rigorism
+degrader mommy rockfish
+deserted moony rugosity
+deserter mummy shamrock
+deserved Nihon Shanghai
+detecter ninny shanghai
+dragster nippy sorority
+dressage nylon syzygial
+eastward nymph ticktock
+excavate onion townsman
+excesses oomph toxicity
+execrate opium turndown
+exegeses phony tutorial
+federate pinky viridity
+freebase pinon Visigoth
+freeware pinup alembic
+rearrest Pliny alright
+rearward plump ambient
+reassert plumy ancient
+reassess plunk Antigua
+recesses polio antique
+recreate Polly apricot
+Redgrave polyp audible
+referrer poppy auditor
+refreeze pulpy augment
+resected pupil auricle
+reserved puppy bicycle
+reserves pylon Blanche
+retarded Union Boeotia
+retarder union buckeye
+reverser unpin chaotic
+reverter Yukon ciboria
+revetted yummy clamant
+scabbard Yupik clangor
+seafarer yuppy Claudia
+seawards hill Clemens
+seawater holy Clement
+seedcase homy clement
+serrated honk Coblenz
+setscrew hook corncob
+sewerage hoop cornrow
+sextette Hopi cowhand
+staggers hulk Cowpens
+stargaze Hull cubicle
+steerage hull cuticle
+stressed hump Dickens
+tattered hunk divisor
+terraced hymn dormant
+tesserae hypo Dubuque
+traverse ikon element
+tweezers inky enchant
+vegetate inly Enfield
+vertebra Ipoh England
+waterbed Jill entitle
+westward jinn figment
diff --git a/vim/golf/50c18a08b855760002000056/1.keys.broken b/vim/golf/50c18a08b855760002000056/1.keys.broken
new file mode 100644
index 0000000..50fbf04
--- /dev/null
+++ b/vim/golf/50c18a08b855760002000056/1.keys.broken
@@ -0,0 +1,8 @@
+j
+VGI
+e
+a<S-Tab><Space><Escape>
+dw
+e
+a<S-Tab><Space><Space><Escape>
+dw \ No newline at end of file
diff --git a/vim/golf/50c18a08b855760002000056/1.ref b/vim/golf/50c18a08b855760002000056/1.ref
new file mode 100644
index 0000000..5330226
--- /dev/null
+++ b/vim/golf/50c18a08b855760002000056/1.ref
@@ -0,0 +1,162 @@
+Left Hand Right Hand Alternating
+desegregated homophony dismantlement
+abracadabra nonillion authenticity
+aftereffect polyphony enchantment
+desegregate homonymy entitlement
+exaggerated Honolulu proficiency
+extraverted lollipop aneurysmal
+resegregate lollypop antisocial
+reverberate Monopoly Dickensian
+abstracted monopoly Kamehameha
+abstracter nonunion producible
+aftertaste Philippi proficient
+afterwards Pilipino Shenandoah
+aggravated polonium skepticism
+Artaxerxes holmium subproblem
+asseverate homonym suspensory
+Basseterre jillion Aleutians
+Bradstreet jollily amendment
+desecrater jumpily antitoxic
+devastated killjoy antivirus
+effervesce kinkily auditoria
+exacerbate Limpopo authentic
+exaggerate lumpily cockfight
+segregated million dirigible
+stagecraft minimum dismantle
+statecraft mullion disorient
+stewardess nonoily divisible
+sweetbread opinion Dixieland
+vertebrate Phillip dizygotic
+watercraft pillion downright
+watercress pumpkin dyspepsia
+abscessed hiphop dyspeptic
+addressee holily endowment
+aftercare homily firmament
+afterward hominy handiwork
+aggravate hookup Malayalam
+aggregate johnny Malaysian
+arrearage Joplin Maldivian
+attracted Kikuyu Mauritian
+barefaced kimono orificial
+beefeater Kunlun pantheism
+castrater limply protozoan
+cerebrate linkup provisory
+crabgrass minion rhapsodic
+Descartes mukluk sudorific
+desecrate muumuu Theodoric
+devastate Nippon tormentor
+eastwards oilily Uruguayan
+eggbeater Philip Vientiane
+extravert phyllo Aleutian
+federated phylum aneurism
+gazetteer pimply aneurysm
+readdress pinion antibody
+rearwards Pinyin Antiguan
+redbreast pinyin auditory
+redresser pinyon blandish
+regretter pippin bodywork
+Sacagawea plummy Boeotian
+sassafras pokily Buchanan
+scattered pompom Burnsian
+scatterer pompon busybody
+segregate poplin busywork
+staggered pullup chairman
+staggerer punily chrismal
+stargazer punkin clansman
+steadfast unholy clemency
+streetcar unhook corpsman
+traverser unlink cyclamen
+Tsvetaeva uphill dormancy
+vertebrae hilly downtick
+Watergate hippo downtown
+westwards hippy downturn
+Zacatecas hokku fiendish
+abstract hokum fishbowl
+acerbate Holly flamenco
+affected holly Futurism
+arrester honky futurism
+artefact hooky futurity
+asserter hulky Ghanaian
+attester hullo handheld
+baccarat hunky handmaid
+Barabbas Ilion Langland
+barrette Ilium Mahayana
+barterer ilium maieutic
+battered imply Malaysia
+batterer Jilin mandible
+bedstead Jimmy Menelaus
+begetter jimmy Nanchang
+bereaved jinni neighbor
+beverage jolly nepenthe
+braggart jumpy neuritic
+Caesarea junky neuritis
+carefree khoum neurosis
+caresser kinky neurotic
+Carreras knoll ornament
+Cascades kooky penchant
+cassette Lippi prorogue
+castrate lippy prosodic
+cataract Lonny protozoa
+crevasse loony prurient
+database loopy quantify
+deadbeat lumpy quantity
+deceased lupin rhapsody
+decrease lymph rifleman
+defeater milky Rightism
+defecate minim rightism
+deferrer Molly rigidity
+degraded molly rigorism
+degrader mommy rockfish
+deserted moony rugosity
+deserter mummy shamrock
+deserved Nihon Shanghai
+detecter ninny shanghai
+dragster nippy sorority
+dressage nylon syzygial
+eastward nymph ticktock
+excavate onion townsman
+excesses oomph toxicity
+execrate opium turndown
+exegeses phony tutorial
+federate pinky viridity
+freebase pinon Visigoth
+freeware pinup alembic
+rearrest Pliny alright
+rearward plump ambient
+reassert plumy ancient
+reassess plunk Antigua
+recesses polio antique
+recreate Polly apricot
+Redgrave polyp audible
+referrer poppy auditor
+refreeze pulpy augment
+resected pupil auricle
+reserved puppy bicycle
+reserves pylon Blanche
+retarded Union Boeotia
+retarder union buckeye
+reverser unpin chaotic
+reverter Yukon ciboria
+revetted yummy clamant
+scabbard Yupik clangor
+seafarer yuppy Claudia
+seawards hill Clemens
+seawater holy Clement
+seedcase homy clement
+serrated honk Coblenz
+setscrew hook corncob
+sewerage hoop cornrow
+sextette Hopi cowhand
+staggers hulk Cowpens
+stargaze Hull cubicle
+steerage hull cuticle
+stressed hump Dickens
+tattered hunk divisor
+terraced hymn dormant
+tesserae hypo Dubuque
+traverse ikon element
+tweezers inky enchant
+vegetate inly Enfield
+vertebra Ipoh England
+waterbed Jill entitle
+westward jinn figment
diff --git a/vim/golf/50c2c246b0544c000200003f/1.in b/vim/golf/50c2c246b0544c000200003f/1.in
new file mode 100644
index 0000000..fcae67c
--- /dev/null
+++ b/vim/golf/50c2c246b0544c000200003f/1.in
@@ -0,0 +1,11 @@
+01234567890123456789012345678901234567890123456789012345678901234567890123456789
+1 0
+2 1
+3 2
+4 3
+5 4
+6 5
+7 6
+8 7
+9 8
+01234567890123456789012345678901234567890123456789012345678901234567890123456789
diff --git a/vim/golf/50c2c246b0544c000200003f/1.ref b/vim/golf/50c2c246b0544c000200003f/1.ref
new file mode 100644
index 0000000..d56793e
--- /dev/null
+++ b/vim/golf/50c2c246b0544c000200003f/1.ref
@@ -0,0 +1,11 @@
+01234567890123456789012345678901234567890123456789012345678901234567890123456789
+1 0 0 0 0 0 0 0 0
+2 1 1 1 1 1 1 1 1
+3 2 2 2 2 2 2 2 2
+4 3 3 3 3 3 3 3 3
+5 4 4 4 4 4 4 4 4
+6 5 5 5 5 5 5 5 5
+7 6 6 6 6 6 6 6 6
+8 7 7 7 7 7 7 7 7
+9 8 8 8 8 8 8 8 8
+01234567890123456789012345678901234567890123456789012345678901234567890123456789
diff --git a/vim/golf/50d0c33daa503f000200000f/1.in b/vim/golf/50d0c33daa503f000200000f/1.in
new file mode 100644
index 0000000..da6f346
--- /dev/null
+++ b/vim/golf/50d0c33daa503f000200000f/1.in
@@ -0,0 +1 @@
+(a) (abc) (abcd) (123456)
diff --git a/vim/golf/50d0c33daa503f000200000f/1.ref b/vim/golf/50d0c33daa503f000200000f/1.ref
new file mode 100644
index 0000000..a0e78fa
--- /dev/null
+++ b/vim/golf/50d0c33daa503f000200000f/1.ref
@@ -0,0 +1 @@
+___ _____ ______ ________
diff --git a/vim/golf/50ee7504c0e3aa0002000040/1.in b/vim/golf/50ee7504c0e3aa0002000040/1.in
new file mode 100644
index 0000000..6882dcc
--- /dev/null
+++ b/vim/golf/50ee7504c0e3aa0002000040/1.in
@@ -0,0 +1 @@
+vimchallenge
diff --git a/vim/golf/50ee7504c0e3aa0002000040/1.ref b/vim/golf/50ee7504c0e3aa0002000040/1.ref
new file mode 100644
index 0000000..c0fe2ae
--- /dev/null
+++ b/vim/golf/50ee7504c0e3aa0002000040/1.ref
@@ -0,0 +1,25 @@
+vimchallenge
+vimchalleng
+vimchallen
+vimchalle
+vimchall
+vimchal
+vimcha
+vimch
+vimc
+vim
+vi
+v
+
+vimchallenge
+imchallenge
+mchallenge
+challenge
+hallenge
+allenge
+llenge
+lenge
+enge
+nge
+ge
+e
diff --git a/vim/golf/51103ad8041832000200003f/1.in b/vim/golf/51103ad8041832000200003f/1.in
new file mode 100644
index 0000000..f027e0d
--- /dev/null
+++ b/vim/golf/51103ad8041832000200003f/1.in
@@ -0,0 +1 @@
+vim
diff --git a/vim/golf/51103ad8041832000200003f/1.ref b/vim/golf/51103ad8041832000200003f/1.ref
new file mode 100644
index 0000000..f7802ff
--- /dev/null
+++ b/vim/golf/51103ad8041832000200003f/1.ref
@@ -0,0 +1,21 @@
+ v v v v v v v v v v v v v v v v v v v v v v
+vimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvim
+ m m m m m m m m m m m m m m m m m m m m m m
+ v v v v v v v v v v v v v v v v v v v v v v
+vimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvim
+ m m m m m m m m m m m m m m m m m m m m m m
+ v v v v v v v v v v v v v v v v v v v v v v
+vimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvim
+ m m m m m m m m m m m m m m m m m m m m m m
+ v v v v v v v v v v v v v v v v v v v v v v
+vimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvim
+ m m m m m m m m m m m m m m m m m m m m m m
+ v v v v v v v v v v v v v v v v v v v v v v
+vimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvim
+ m m m m m m m m m m m m m m m m m m m m m m
+ v v v v v v v v v v v v v v v v v v v v v v
+vimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvim
+ m m m m m m m m m m m m m m m m m m m m m m
+ v v v v v v v v v v v v v v v v v v v v v v
+vimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvimvim
+ m m m m m m m m m m m m m m m m m m m m m m
diff --git a/vim/golf/5192f96ad8df110002000002/1.in b/vim/golf/5192f96ad8df110002000002/1.in
new file mode 100644
index 0000000..7e6e08d
--- /dev/null
+++ b/vim/golf/5192f96ad8df110002000002/1.in
@@ -0,0 +1,2 @@
+one two
+three
diff --git a/vim/golf/5192f96ad8df110002000002/1.ref b/vim/golf/5192f96ad8df110002000002/1.ref
new file mode 100644
index 0000000..729970d
--- /dev/null
+++ b/vim/golf/5192f96ad8df110002000002/1.ref
@@ -0,0 +1,2 @@
+(one) (two)
+(three)
diff --git a/vim/golf/524e1a20b81fe50002000008/1.in b/vim/golf/524e1a20b81fe50002000008/1.in
new file mode 100644
index 0000000..d8e8a75
--- /dev/null
+++ b/vim/golf/524e1a20b81fe50002000008/1.in
@@ -0,0 +1,3 @@
+attr_reader :align, :variables, :with
+attr_accessor :spaces, :to, :complete, :challenge
+# vim: set expandtab:
diff --git a/vim/golf/524e1a20b81fe50002000008/1.ref b/vim/golf/524e1a20b81fe50002000008/1.ref
new file mode 100644
index 0000000..c7d11b1
--- /dev/null
+++ b/vim/golf/524e1a20b81fe50002000008/1.ref
@@ -0,0 +1,8 @@
+attr_reader :align,
+ :variables,
+ :with
+attr_accessor :spaces,
+ :to,
+ :complete,
+ :challenge
+# vim: set expandtab:
diff --git a/vim/golf/52c3cb0d9b8634000200000e/1.in b/vim/golf/52c3cb0d9b8634000200000e/1.in
new file mode 100644
index 0000000..41b33e6
--- /dev/null
+++ b/vim/golf/52c3cb0d9b8634000200000e/1.in
@@ -0,0 +1 @@
+A HAPPY END WITH YEAR 2013 !
diff --git a/vim/golf/52c3cb0d9b8634000200000e/1.ref b/vim/golf/52c3cb0d9b8634000200000e/1.ref
new file mode 100644
index 0000000..40856ae
--- /dev/null
+++ b/vim/golf/52c3cb0d9b8634000200000e/1.ref
@@ -0,0 +1 @@
+A HAPPY NEW YEAR 2014 !
diff --git a/vim/golf/53369b712a09c1000223fb57/1.in b/vim/golf/53369b712a09c1000223fb57/1.in
new file mode 100644
index 0000000..4659403
--- /dev/null
+++ b/vim/golf/53369b712a09c1000223fb57/1.in
@@ -0,0 +1,15 @@
+/* Frame (32 bytes) */
+static const unsigned char pkt1[32] = {
+0x08, 0x60, 0x6e, 0xf1, 0x3c, 0xb9, 0x9c, 0xc7, /* .`n.<... */
+0xa6, 0x35, 0x08, 0x12, 0x08, 0x00, 0x45, 0x00, /* .5....E. */
+0x05, 0xdc, 0x1b, 0xe3, 0x40, 0x00, 0x31, 0x06, /* ....@.1. */
+0xe8, 0xc8, 0xcb, 0xd9, 0x00, 0xda, 0xc0, 0xa8 /* ........ */
+};
+
+/* Frame (32 bytes) */
+static const unsigned char pkt2[32] = {
+0x08, 0x60, 0x6e, 0xf1, 0x3c, 0xb9, 0x9c, 0xc7, /* .`n.<... */
+0xa6, 0x35, 0x08, 0x12, 0x08, 0x00, 0x45, 0x00, /* .5....E. */
+0x05, 0xdc, 0x6b, 0x98, 0x40, 0x00, 0x31, 0x06, /* ..k.@.1. */
+0x99, 0x13, 0xcb, 0xd9, 0x00, 0xda, 0xc0, 0xa8, /* ........ */
+};
diff --git a/vim/golf/53369b712a09c1000223fb57/1.ref b/vim/golf/53369b712a09c1000223fb57/1.ref
new file mode 100644
index 0000000..901be5c
--- /dev/null
+++ b/vim/golf/53369b712a09c1000223fb57/1.ref
@@ -0,0 +1,15 @@
+/* Frame (32 bytes) */
+static const unsigned char pkt1[2][32] = {
+ {
+ 0x08, 0x60, 0x6e, 0xf1, 0x3c, 0xb9, 0x9c, 0xc7, /* .`n.<... */
+ 0xa6, 0x35, 0x08, 0x12, 0x08, 0x00, 0x45, 0x00, /* .5....E. */
+ 0x05, 0xdc, 0x1b, 0xe3, 0x40, 0x00, 0x31, 0x06, /* ....@.1. */
+ 0xe8, 0xc8, 0xcb, 0xd9, 0x00, 0xda, 0xc0, 0xa8 /* ........ */
+ },
+ {
+ 0x08, 0x60, 0x6e, 0xf1, 0x3c, 0xb9, 0x9c, 0xc7, /* .`n.<... */
+ 0xa6, 0x35, 0x08, 0x12, 0x08, 0x00, 0x45, 0x00, /* .5....E. */
+ 0x05, 0xdc, 0x6b, 0x98, 0x40, 0x00, 0x31, 0x06, /* ..k.@.1. */
+ 0x99, 0x13, 0xcb, 0xd9, 0x00, 0xda, 0xc0, 0xa8, /* ........ */
+ }
+};
diff --git a/vim/golf/536cfa23fcccd100025678bd/1.in b/vim/golf/536cfa23fcccd100025678bd/1.in
new file mode 100644
index 0000000..2085e46
--- /dev/null
+++ b/vim/golf/536cfa23fcccd100025678bd/1.in
@@ -0,0 +1 @@
+attr("y",function(v){return v})
diff --git a/vim/golf/536cfa23fcccd100025678bd/1.ref b/vim/golf/536cfa23fcccd100025678bd/1.ref
new file mode 100644
index 0000000..dde279f
--- /dev/null
+++ b/vim/golf/536cfa23fcccd100025678bd/1.ref
@@ -0,0 +1,2 @@
+f=function(v){return v};
+attr("y",f)
diff --git a/vim/golf/537a553282aa3e000222048a/1.in b/vim/golf/537a553282aa3e000222048a/1.in
new file mode 100644
index 0000000..d58dfaa
--- /dev/null
+++ b/vim/golf/537a553282aa3e000222048a/1.in
@@ -0,0 +1,14 @@
+<table>
+<thead>
+<th>
+<th id="first_name">First Name</th>
+<th id="last_name">Last Name</th>
+<th id="address">Address</th>
+<th id="city">City</th>
+<th id="state">State</th>
+<th id="zip_postal">Zip</th>
+<th id="country">Country</th>
+<th id="phone">Phone</th>
+</th>
+</thead>
+</table>
diff --git a/vim/golf/537a553282aa3e000222048a/1.ref b/vim/golf/537a553282aa3e000222048a/1.ref
new file mode 100644
index 0000000..5ec0884
--- /dev/null
+++ b/vim/golf/537a553282aa3e000222048a/1.ref
@@ -0,0 +1,14 @@
+<table>
+ <thead>
+ <th>
+ <th id="first_name"> First Name</th>
+ <th id="last_name "> Last Name </th>
+ <th id="address "> Address </th>
+ <th id="city "> City </th>
+ <th id="state "> State </th>
+ <th id="zip_postal"> Zip </th>
+ <th id="country "> Country </th>
+ <th id="phone "> Phone </th>
+ </th>
+ </thead>
+</table>
diff --git a/vim/golf/53d93fc3768e280002124f23/1.in b/vim/golf/53d93fc3768e280002124f23/1.in
new file mode 100644
index 0000000..6c66521
--- /dev/null
+++ b/vim/golf/53d93fc3768e280002124f23/1.in
@@ -0,0 +1 @@
+VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
diff --git a/vim/golf/53d93fc3768e280002124f23/1.ref b/vim/golf/53d93fc3768e280002124f23/1.ref
new file mode 100644
index 0000000..64101a6
--- /dev/null
+++ b/vim/golf/53d93fc3768e280002124f23/1.ref
@@ -0,0 +1 @@
+iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
diff --git a/vim/golf/54595b13128576000257a3c1/1.in b/vim/golf/54595b13128576000257a3c1/1.in
new file mode 100644
index 0000000..c29078f
--- /dev/null
+++ b/vim/golf/54595b13128576000257a3c1/1.in
@@ -0,0 +1,6 @@
+10 PRINT "The actual"
+15 PRINT "code doesn't"
+16 PRINT "really matter."
+20 PRINT "Just take"
+25 PRINT "care of"
+30 PRINT "the numbers."
diff --git a/vim/golf/54595b13128576000257a3c1/1.ref b/vim/golf/54595b13128576000257a3c1/1.ref
new file mode 100644
index 0000000..c9a8e5b
--- /dev/null
+++ b/vim/golf/54595b13128576000257a3c1/1.ref
@@ -0,0 +1,6 @@
+10 PRINT "The actual"
+20 PRINT "code doesn't"
+30 PRINT "really matter."
+40 PRINT "Just take"
+50 PRINT "care of"
+60 PRINT "the numbers."
diff --git a/vim/golf/5462e3f41198b80002512673/1.in b/vim/golf/5462e3f41198b80002512673/1.in
new file mode 100644
index 0000000..9a57bce
--- /dev/null
+++ b/vim/golf/5462e3f41198b80002512673/1.in
@@ -0,0 +1,3 @@
+foo = a
+ ab
+ abc
diff --git a/vim/golf/5462e3f41198b80002512673/1.ref b/vim/golf/5462e3f41198b80002512673/1.ref
new file mode 100644
index 0000000..3e25168
--- /dev/null
+++ b/vim/golf/5462e3f41198b80002512673/1.ref
@@ -0,0 +1,3 @@
+foo = "a"
+ "ab"
+ "abc"
diff --git a/vim/golf/54698da795f6da00020d85ed/1.in b/vim/golf/54698da795f6da00020d85ed/1.in
new file mode 100644
index 0000000..1fc4f34
--- /dev/null
+++ b/vim/golf/54698da795f6da00020d85ed/1.in
@@ -0,0 +1,6 @@
+enum PlaybackRequestType {
+ case Next
+ case Previous
+ case Play
+ case Stop
+}
diff --git a/vim/golf/54698da795f6da00020d85ed/1.ref b/vim/golf/54698da795f6da00020d85ed/1.ref
new file mode 100644
index 0000000..9e8f420
--- /dev/null
+++ b/vim/golf/54698da795f6da00020d85ed/1.ref
@@ -0,0 +1,3 @@
+enum PlaybackRequestType {
+ case Next, Previous, Play, Stop
+}
diff --git a/vim/golf/54862fbb3f90ac0002904cf5/1.in b/vim/golf/54862fbb3f90ac0002904cf5/1.in
new file mode 100644
index 0000000..e47b2ef
--- /dev/null
+++ b/vim/golf/54862fbb3f90ac0002904cf5/1.in
@@ -0,0 +1,8 @@
+Leave only the
+numbered lines.
+LINE 1
+LINE 2
+LINE 3
+That's all.
+Thank you
+very much.
diff --git a/vim/golf/54862fbb3f90ac0002904cf5/1.ref b/vim/golf/54862fbb3f90ac0002904cf5/1.ref
new file mode 100644
index 0000000..bc30bb7
--- /dev/null
+++ b/vim/golf/54862fbb3f90ac0002904cf5/1.ref
@@ -0,0 +1,3 @@
+LINE 1
+LINE 2
+LINE 3
diff --git a/vim/golf/55771cc4750ef86573003b83/1.in b/vim/golf/55771cc4750ef86573003b83/1.in
new file mode 100644
index 0000000..63b0445
--- /dev/null
+++ b/vim/golf/55771cc4750ef86573003b83/1.in
@@ -0,0 +1,11 @@
+foo {
+ a => "a",
+ bc => "bc",
+ def => "def",
+ ghij => "ghij",
+ k => "k",
+ lmn => "lmn",
+ opqrst => "opqrst",
+ uvw => "uvw",
+ xyz => "xyz",
+}
diff --git a/vim/golf/55771cc4750ef86573003b83/1.ref b/vim/golf/55771cc4750ef86573003b83/1.ref
new file mode 100644
index 0000000..d8af11c
--- /dev/null
+++ b/vim/golf/55771cc4750ef86573003b83/1.ref
@@ -0,0 +1,11 @@
+foo {
+ a => "a",
+ bc => "bc",
+ def => "def",
+ ghij => "ghij",
+ k => "k",
+ lmn => "lmn",
+ opqrst => "opqrst",
+ uvw => "uvw",
+ xyz => "xyz",
+}
diff --git a/vim/golf/559c30948ef59c0eb7000002/1.in b/vim/golf/559c30948ef59c0eb7000002/1.in
new file mode 100644
index 0000000..05a126f
--- /dev/null
+++ b/vim/golf/559c30948ef59c0eb7000002/1.in
@@ -0,0 +1,5 @@
+* item1
+* item2
+* item3
+* item4
+* item5
diff --git a/vim/golf/559c30948ef59c0eb7000002/1.ref b/vim/golf/559c30948ef59c0eb7000002/1.ref
new file mode 100644
index 0000000..6c475a7
--- /dev/null
+++ b/vim/golf/559c30948ef59c0eb7000002/1.ref
@@ -0,0 +1 @@
+item1,item2,item3,item4,item5
diff --git a/vim/golf/55b18bbea9c2c30d04000001/1.in b/vim/golf/55b18bbea9c2c30d04000001/1.in
new file mode 100644
index 0000000..9ffc30d
--- /dev/null
+++ b/vim/golf/55b18bbea9c2c30d04000001/1.in
@@ -0,0 +1,10 @@
+*temp var1 0
+*temp var2 "hi"
+*temp var3 -1
+*temp var4 42
+*temp var5 "asdf"
+*temp var6 0
+
+Simple things we do all the time should be able to be done with very few keystrokes, but sometimes I find something I need to do makes me go, "There MUST be a better way."
+
+This challenge is just a simple movement and entering text at a certain place.
diff --git a/vim/golf/55b18bbea9c2c30d04000001/1.ref b/vim/golf/55b18bbea9c2c30d04000001/1.ref
new file mode 100644
index 0000000..514075f
--- /dev/null
+++ b/vim/golf/55b18bbea9c2c30d04000001/1.ref
@@ -0,0 +1,13 @@
+*temp var1 0
+*temp var2 "hi"
+*temp var3 -1
+*temp var4 42
+*temp var5 "asdf"
+*temp var6 0
+*temp var7 11
+
+Simple things we do all the time should be able to be done with very few keystrokes, but sometimes I find something I need to do makes me go, "There MUST be a better way."
+
+New text.
+
+This challenge is just a simple movement and entering text at a certain place.
diff --git a/vim/golf/55bcdc3ef4219f456102374f/1.in b/vim/golf/55bcdc3ef4219f456102374f/1.in
new file mode 100644
index 0000000..2fe6575
--- /dev/null
+++ b/vim/golf/55bcdc3ef4219f456102374f/1.in
@@ -0,0 +1 @@
+The quick brown fox jumps over the lazy dog.
diff --git a/vim/golf/55bcdc3ef4219f456102374f/1.ref b/vim/golf/55bcdc3ef4219f456102374f/1.ref
new file mode 100644
index 0000000..356d6fd
--- /dev/null
+++ b/vim/golf/55bcdc3ef4219f456102374f/1.ref
@@ -0,0 +1 @@
+The quick lazy dog jumps over the brown fox.
diff --git a/vim/golf/55f9720b4a665c2acf0008c8/1.in b/vim/golf/55f9720b4a665c2acf0008c8/1.in
new file mode 100644
index 0000000..bc52eec
--- /dev/null
+++ b/vim/golf/55f9720b4a665c2acf0008c8/1.in
@@ -0,0 +1,4 @@
+Lôrem Ipsum
+Dolor.eros
+Auctor: eros (elémentum)
+Tincïdunt, âc
diff --git a/vim/golf/55f9720b4a665c2acf0008c8/1.ref b/vim/golf/55f9720b4a665c2acf0008c8/1.ref
new file mode 100644
index 0000000..5a2aa55
--- /dev/null
+++ b/vim/golf/55f9720b4a665c2acf0008c8/1.ref
@@ -0,0 +1,8 @@
+lorem-ipsum:
+ name: "Lôrem Ipsum"
+dolor-eros:
+ name: "Dolor.eros"
+auctor-eros-elementum:
+ name: "Auctor: eros (elémentum)"
+tincidunt-ac:
+ name: "Tincïdunt, âc"
diff --git a/vim/operators/case/case.in b/vim/operators/case/case.in
new file mode 100644
index 0000000..b952c26
--- /dev/null
+++ b/vim/operators/case/case.in
@@ -0,0 +1 @@
+Abc
diff --git a/vim/operators/case/case.keys b/vim/operators/case/case.keys
new file mode 100644
index 0000000..ab6d40b
--- /dev/null
+++ b/vim/operators/case/case.keys
@@ -0,0 +1 @@
+gu gU .
diff --git a/vim/operators/change/change.in b/vim/operators/change/change.in
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/vim/operators/change/change.in
@@ -0,0 +1 @@
+hello world
diff --git a/vim/operators/change/change.keys b/vim/operators/change/change.keys
new file mode 100644
index 0000000..d9f8937
--- /dev/null
+++ b/vim/operators/change/change.keys
@@ -0,0 +1 @@
+cwfoo<Escape>w.
diff --git a/vim/operators/delete/delete.in b/vim/operators/delete/delete.in
new file mode 100644
index 0000000..d9bc611
--- /dev/null
+++ b/vim/operators/delete/delete.in
@@ -0,0 +1,5 @@
+one two
+three four
+five six
+seven eigth
+nine ten
diff --git a/vim/operators/delete/delete.keys b/vim/operators/delete/delete.keys
new file mode 100644
index 0000000..20bb113
--- /dev/null
+++ b/vim/operators/delete/delete.keys
@@ -0,0 +1,2 @@
+/two<Enter>
+dvjd2j
diff --git a/vim/operators/join/join.in b/vim/operators/join/join.in
new file mode 100644
index 0000000..8a1218a
--- /dev/null
+++ b/vim/operators/join/join.in
@@ -0,0 +1,5 @@
+1
+2
+3
+4
+5
diff --git a/vim/operators/join/join.keys b/vim/operators/join/join.keys
new file mode 100644
index 0000000..2476f31
--- /dev/null
+++ b/vim/operators/join/join.keys
@@ -0,0 +1 @@
+3J
diff --git a/vim/operators/put/put.in b/vim/operators/put/put.in
new file mode 100644
index 0000000..bd46cf2
--- /dev/null
+++ b/vim/operators/put/put.in
@@ -0,0 +1 @@
+one two
diff --git a/vim/operators/put/put.keys b/vim/operators/put/put.keys
new file mode 100644
index 0000000..5c20233
--- /dev/null
+++ b/vim/operators/put/put.keys
@@ -0,0 +1 @@
+ywPw3Pw.
diff --git a/vim/operators/shift/shift.in b/vim/operators/shift/shift.in
new file mode 100644
index 0000000..9c54329
--- /dev/null
+++ b/vim/operators/shift/shift.in
@@ -0,0 +1,2 @@
+one two
+ one two
diff --git a/vim/operators/shift/shift.keys b/vim/operators/shift/shift.keys
new file mode 100644
index 0000000..b157798
--- /dev/null
+++ b/vim/operators/shift/shift.keys
@@ -0,0 +1 @@
+>>j<<
diff --git a/vim/operators/swap/swap.in b/vim/operators/swap/swap.in
new file mode 100644
index 0000000..74ec2e7
--- /dev/null
+++ b/vim/operators/swap/swap.in
@@ -0,0 +1,3 @@
+abc
+abc
+abc
diff --git a/vim/operators/swap/swap.keys b/vim/operators/swap/swap.keys
new file mode 100644
index 0000000..cad6d31
--- /dev/null
+++ b/vim/operators/swap/swap.keys
@@ -0,0 +1 @@
+~l.wV~w2~
diff --git a/vim/operators/yank/yank.in b/vim/operators/yank/yank.in
new file mode 100644
index 0000000..bd46cf2
--- /dev/null
+++ b/vim/operators/yank/yank.in
@@ -0,0 +1 @@
+one two
diff --git a/vim/operators/yank/yank.keys b/vim/operators/yank/yank.keys
new file mode 100644
index 0000000..9098f4e
--- /dev/null
+++ b/vim/operators/yank/yank.keys
@@ -0,0 +1 @@
+y$P0yyp..
diff --git a/vim/prompt/history.in b/vim/prompt/history.in
new file mode 100644
index 0000000..a92d664
--- /dev/null
+++ b/vim/prompt/history.in
@@ -0,0 +1,3 @@
+line 1
+line 2
+line 3
diff --git a/vim/prompt/history.keys b/vim/prompt/history.keys
new file mode 100644
index 0000000..da0b6ac
--- /dev/null
+++ b/vim/prompt/history.keys
@@ -0,0 +1,6 @@
+:+-s/line/word/<Enter> /* replace something on first line */
+:2<Enter> /* select second line */
+:<Up><Up><Enter> /* repeat replacement */
+:e<Backspace><Backspace> /* simulate a typo on the command line */
+:3<Enter> /* select third line */
+:<Up><Up><Enter> /* repeat replacement */
diff --git a/vim/test.sh b/vim/test.sh
new file mode 100755
index 0000000..3734268
--- /dev/null
+++ b/vim/test.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+[ -z "$VIS" ] && VIS="../../vis"
+[ -z "$VIM" ] && VIM="vim"
+
+EDITORS="$VIM $VIS"
+
+TESTS=$1
+[ -z "$TESTS" ] && TESTS=$(find . -name '*.keys' | sed 's/\.keys$//g')
+
+TESTS_RUN=0
+TESTS_OK=0
+
+$VIM --version | head -1
+$VIS -v
+
+for t in $TESTS; do
+ for EDITOR in $EDITORS; do
+ e=$(basename "$EDITOR");
+ ERR="$t.$e.err"
+ OUT="$t.$e.out"
+ REF="$t.ref"
+ VIM_OUT="$t.$VIM.out"
+ printf "Running test %s with %s ... " "$t" "$e"
+ rm -f "$OUT" "$ERR"
+ { cat "$t.keys"; printf "<Escape>:wq! $OUT<Enter>"; } | cpp -P 2>/dev/null | ../util/keys | $EDITOR "$t.in" 2> /dev/null
+ if [ "$e" = "$VIM" ]; then
+ if [ -e "$REF" ]; then
+ if cmp -s "$REF" "$OUT"; then
+ printf "OK\n"
+ else
+ printf "FAIL\n"
+ diff -u "$REF" "$OUT" > "$ERR"
+ fi
+ elif [ -e "$VIM_OUT" ]; then
+ printf "OK\n"
+ else
+ printf "FAIL\n"
+ fi
+ elif [ -e "$REF" -o -e "$VIM_OUT" ]; then
+ [ -e "$VIM_OUT" ] && REF="$VIM_OUT"
+ if cmp -s "$REF" "$OUT"; then
+ printf "OK\n"
+ TESTS_OK=$((TESTS_OK+1))
+ else
+ printf "FAIL\n"
+ diff -u "$REF" "$OUT" > "$ERR"
+ fi
+ TESTS_RUN=$((TESTS_RUN+1))
+ fi
+ done
+done
+
+printf "Tests ok %d/%d\n" $TESTS_OK $TESTS_RUN
+
+# set exit status
+[ $TESTS_OK -eq $TESTS_RUN ]
diff --git a/vim/text-objects/braces-count-linewise.in b/vim/text-objects/braces-count-linewise.in
new file mode 100644
index 0000000..069493e
--- /dev/null
+++ b/vim/text-objects/braces-count-linewise.in
@@ -0,0 +1,5 @@
+struct {
+ union {
+ int foo;
+ };
+};
diff --git a/vim/text-objects/braces-count-linewise.keys b/vim/text-objects/braces-count-linewise.keys
new file mode 100644
index 0000000..0ab2b26
--- /dev/null
+++ b/vim/text-objects/braces-count-linewise.keys
@@ -0,0 +1,2 @@
+/foo<Enter>
+d2Va}
diff --git a/vis/.gitignore b/vis/.gitignore
new file mode 100644
index 0000000..589a89a
--- /dev/null
+++ b/vis/.gitignore
@@ -0,0 +1,3 @@
+*.out
+*.err
+*.disabled \ No newline at end of file
diff --git a/vis/Makefile b/vis/Makefile
new file mode 100644
index 0000000..36a4ce7
--- /dev/null
+++ b/vis/Makefile
@@ -0,0 +1,15 @@
+test: ../../vis ../util/keys
+ @./test.sh
+
+../../vis: ../../*.[ch]
+ @echo Compiling vis
+ @$(MAKE) -C ../..
+
+../util/keys: ../util/keys.c
+ @$(MAKE) -C ../util
+
+clean:
+ @echo cleaning
+ @find . -name '*.out' -o -name '*.err' | xargs rm -f
+
+.PHONY: clean test
diff --git a/vis/README.md b/vis/README.md
new file mode 100644
index 0000000..e7e655d
--- /dev/null
+++ b/vis/README.md
@@ -0,0 +1,17 @@
+Tests for vis specific editing features
+---------------------------------------
+
+The basic idea is to feed keyboard input to `vis` and compare the
+produced output with a known reference solution.
+
+A test constitutes of 3 files:
+
+ * `test.in` the file/buffer content with which the editor is started
+ * `test.keys` a file containing the keyboard input as would normally
+ be typed by a user
+ * `test.ref` a reference file at the end of the editing session
+
+The toplevel shell script `test.sh` looks for these files in sub
+directories, feeds the keys to `vis` and compares the produces output.
+
+Type `make` to run all tests.
diff --git a/vis/multiple-cursors/basic.in b/vis/multiple-cursors/basic.in
new file mode 100644
index 0000000..949ea37
--- /dev/null
+++ b/vis/multiple-cursors/basic.in
@@ -0,0 +1,4 @@
+1 : first
+2 : second
+3 : third
+4 : fourth
diff --git a/vis/multiple-cursors/basic.keys b/vis/multiple-cursors/basic.keys
new file mode 100644
index 0000000..df8738e
--- /dev/null
+++ b/vis/multiple-cursors/basic.keys
@@ -0,0 +1,6 @@
+vGI /* create cursor at start of every line */
+df<Space> /* delete to first space */
+. /* delete to second space */
+A : end<Escape> /* append " : end" to line */
+. /* repeat */
+u /* undo */
diff --git a/vis/multiple-cursors/basic.ref b/vis/multiple-cursors/basic.ref
new file mode 100644
index 0000000..7c5bd13
--- /dev/null
+++ b/vis/multiple-cursors/basic.ref
@@ -0,0 +1,4 @@
+first : end
+second : end
+third : end
+fourth : end
diff --git a/vis/test.sh b/vis/test.sh
new file mode 100755
index 0000000..a52fe0f
--- /dev/null
+++ b/vis/test.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+[ -z "$VIS" ] && VIS="../../vis"
+
+TESTS=$1
+[ -z "$TESTS" ] && TESTS=$(find . -name '*.keys' | sed 's/\.keys$//g')
+
+TESTS_RUN=0
+TESTS_OK=0
+
+$VIS -v
+
+for t in $TESTS; do
+ ERR="$t.err"
+ OUT="$t.out"
+ REF="$t.ref"
+ printf "Running test %s ... " "$t"
+ rm -f "$OUT" "$ERR"
+ { cat "$t.keys"; printf "<Escape>:wq! $OUT<Enter>"; } | cpp -P | ../util/keys | $VIS "$t.in" 2> /dev/null
+ if [ -e "$OUT" ]; then
+ if cmp -s "$REF" "$OUT"; then
+ printf "OK\n"
+ TESTS_OK=$((TESTS_OK+1))
+ else
+ printf "FAIL\n"
+ diff -u "$REF" "$OUT" > "$ERR"
+ fi
+ TESTS_RUN=$((TESTS_RUN+1))
+ fi
+done
+
+printf "Tests ok %d/%d\n" $TESTS_OK $TESTS_RUN
+
+# set exit status
+[ $TESTS_OK -eq $TESTS_RUN ]