]> git.ozlabs.org Git - ccan/commitdiff
base64: fix for unsigned chars (e.g. ARM). master
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 1 Aug 2023 01:43:53 +0000 (11:13 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 1 Aug 2023 01:43:53 +0000 (11:13 +0930)
```
ccan/ccan/base64/base64.c:34:10: error: result of comparison of constant 255 with expression of type 'int8_t' (aka 'signed char') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
        if (ret == (char)0xff) {
            ~~~ ^  ~~~~~~~~~~
ccan/ccan/base64/base64.c:44:57: error: result of comparison of constant 255 with expression of type 'const signed char' is always true [-Werror,-Wtautological-constant-out-of-range-compare]
        return (maps->decode_map[(const unsigned char)b64char] != (char)0xff);
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~
```

Reported-by: Christian Decker
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
110 files changed:
Makefile
Makefile-web
ccan/antithread/alloc/alloc.c
ccan/antithread/alloc/tiny.c
ccan/base64/base64.c
ccan/base64/base64.h
ccan/bitops/test/run.c
ccan/bytestring/bytestring.h
ccan/closefrom/LICENSE [new symlink]
ccan/closefrom/_info [new file with mode: 0644]
ccan/closefrom/closefrom.c [new file with mode: 0644]
ccan/closefrom/closefrom.h [new file with mode: 0644]
ccan/closefrom/test/run.c [new file with mode: 0644]
ccan/compiler/compiler.h
ccan/crc32c/LICENSE [new symlink]
ccan/crc32c/crc32c.c
ccan/crypto/hmac_sha256/hmac_sha256.c
ccan/crypto/xtea/LICENSE [new symlink]
ccan/crypto/xtea/xtea.c
ccan/daemonize/daemonize.c
ccan/darray/darray.h
ccan/edit_distance/edit_distance_dl.c
ccan/edit_distance/edit_distance_lcs.c
ccan/edit_distance/edit_distance_lev.c
ccan/edit_distance/edit_distance_rdl.c
ccan/failtest/failtest.c
ccan/graphql/LICENSE [new symlink]
ccan/graphql/_info [new file with mode: 0644]
ccan/graphql/graphql.c [new file with mode: 0644]
ccan/graphql/graphql.h [new file with mode: 0644]
ccan/graphql/test/run.c [new file with mode: 0644]
ccan/htable/htable.c
ccan/htable/htable.h
ccan/htable/htable_type.h
ccan/htable/test/run-clash.c [new file with mode: 0644]
ccan/htable/test/run-debug.c
ccan/htable/test/run.c
ccan/htable/tools/Makefile
ccan/htable/tools/density.c [new file with mode: 0644]
ccan/idtree/idtree.c
ccan/ilog/ilog.h
ccan/io/fdpass/_info
ccan/io/io.c
ccan/io/io.h
ccan/io/poll.c
ccan/json/json.h
ccan/json_escape/json_escape.c
ccan/json_out/json_out.c
ccan/json_out/json_out.h
ccan/mem/mem.h
ccan/membuf/_info
ccan/net/net.c
ccan/objset/_info
ccan/objset/objset.h
ccan/opt/helpers.c
ccan/opt/opt.c
ccan/opt/opt.h
ccan/opt/parse.c
ccan/opt/test/run-add_desc.c
ccan/opt/test/run-correct-reporting.c
ccan/opt/test/run-helpers.c
ccan/opt/test/run-set_alloc.c
ccan/opt/test/run-userbits.c [new file with mode: 0644]
ccan/opt/test/utils.c
ccan/opt/test/utils.h
ccan/opt/usage.c
ccan/pipecmd/_info
ccan/pipecmd/pipecmd.c
ccan/rbuf/rbuf.c
ccan/rune/LICENSE [new symlink]
ccan/rune/_info [new file with mode: 0644]
ccan/rune/coding.c [new file with mode: 0644]
ccan/rune/internal.h [new file with mode: 0644]
ccan/rune/rune.c [new file with mode: 0644]
ccan/rune/rune.h [new file with mode: 0644]
ccan/rune/test/run-alt-lexicographic-order.c [new file with mode: 0644]
ccan/rune/test/run-altern-escape.c [new file with mode: 0644]
ccan/rune/test/run.c [new file with mode: 0644]
ccan/rune/test/test_vectors.csv [new file with mode: 0644]
ccan/str/base32/base32.c
ccan/stringbuilder/stringbuilder.c
ccan/strmap/strmap.c
ccan/strmap/strmap.h
ccan/tal/tal.c
ccan/tal/tal.h
ccan/tal/test/run-notifier.c
ccan/tally/tally.c
ccan/tap/tap.c
ccan/tap/tap.h
ccan/tcon/tcon.h
ccan/tcon/test/compile_fail-container1.c
ccan/tcon/test/compile_fail-container1w.c
ccan/tcon/test/compile_fail-container3.c
ccan/tcon/test/compile_fail-container3w.c
ccan/timer/timer.c
ccan/ungraph/LICENSE [new symlink]
ccan/ungraph/_info [new file with mode: 0644]
ccan/ungraph/test/run.c [new file with mode: 0644]
ccan/ungraph/ungraph.c [new file with mode: 0644]
ccan/ungraph/ungraph.h [new file with mode: 0644]
ccan/utf8/test/run-decode.c
ccan/utf8/utf8.c
ccan/utf8/utf8.h
ccan/version/version.h
tools/ccanlint/async.c
tools/ccanlint/licenses.h
tools/configurator/configurator.c
tools/read_config_header.c
web/staticall.php
web/staticindex.php

index d53e89f4d1ca9556e64187e4d3a1a63272b64c74..875e99f2146616ce41abb8bcdb6813ebdb50a53f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ all::
 
 # Our flags for building
 WARN_CFLAGS := -Wall -Wstrict-prototypes -Wold-style-definition -Wundef \
 
 # Our flags for building
 WARN_CFLAGS := -Wall -Wstrict-prototypes -Wold-style-definition -Wundef \
- -Wmissing-prototypes -Wmissing-declarations -Wpointer-arith -Wwrite-strings
+ -Wmissing-prototypes -Wmissing-declarations -Wpointer-arith -Wwrite-strings -Wshadow=local
 DEP_CFLAGS = -MMD -MP -MF$(@:%=%.d) -MT$@
 CCAN_CFLAGS := -g3 -ggdb $(WARN_CFLAGS) -DCCAN_STR_DEBUG=1 -I. $(CFLAGS)
 CFLAGS_FORCE_C_SOURCE := -x c
 DEP_CFLAGS = -MMD -MP -MF$(@:%=%.d) -MT$@
 CCAN_CFLAGS := -g3 -ggdb $(WARN_CFLAGS) -DCCAN_STR_DEBUG=1 -I. $(CFLAGS)
 CFLAGS_FORCE_C_SOURCE := -x c
index 29ca254e635701eed3c20ff44dd3a6661ec83bf5..ae388c0cadfde215b5291ca79663ca7378b496d5 100644 (file)
@@ -18,6 +18,7 @@ JUNKPAGES=$(JUNKDIRS:%=$(WEBDIR)/%.html)
 JUNKBALLS=$(JUNKDIRS:%=$(WEBDIR)/%.tar.bz2)
 PRETTIFY=$(WEBDIR)/prettify/src/run_prettify.js $(WEBDIR)/prettify/src/prettify.css
 
 JUNKBALLS=$(JUNKDIRS:%=$(WEBDIR)/%.tar.bz2)
 PRETTIFY=$(WEBDIR)/prettify/src/run_prettify.js $(WEBDIR)/prettify/src/prettify.css
 
+VERSION_NUM := $(shell git describe | cut -d- -f2)
 
 clean-tree:
        [ "$(WEBDEV)" ] || ! git status --porcelain | grep .
 
 clean-tree:
        [ "$(WEBDEV)" ] || ! git status --porcelain | grep .
@@ -40,14 +41,17 @@ $(WEBDIR)/junkcode/%.html: $(WEBDIR)/junkcode/%.tar.bz2
        cd $(WEBDIR) && tar xfj junkcode/$*.tar.bz2
        URLPREFIX=../ $(PHP) web/staticjunkcode.php junkcode/$* $* > $@
 
        cd $(WEBDIR) && tar xfj junkcode/$*.tar.bz2
        URLPREFIX=../ $(PHP) web/staticjunkcode.php junkcode/$* $* > $@
 
+$(WEBDIR)/ccan.tar.bz2: $(WEBDIR)/ccan-$(VERSION_NUM).tar.bz2
+       ln -sf ccan-$(VERSION_NUM).tar.bz2 ccan.tar.bz2
+
 # We want tarball to contain ccan/; we put junkcode in, but don't depend on it.
 # We want tarball to contain ccan/; we put junkcode in, but don't depend on it.
-$(WEBDIR)/ccan.tar.bz2: config.h Makefile $(shell git ls-files ccan tools licenses)
+$(WEBDIR)/ccan-$(VERSION_NUM).tar.bz2: config.h Makefile $(shell git ls-files ccan tools licenses)
        DIR=`pwd` && cd /tmp && ln -sf "$$DIR" ccan && tar cvfj $@ `for f in $^; do echo ccan/$$f; done` ccan/junkcode && rm ccan
 
 $(ALL_PAGES): tools/doc_extract web/staticmoduleinfo.php
 
 $(WEBDIR)/list.html: web/staticall.php tools/doc_extract $(DIRECT_TARBALLS) $(DEPEND_TARBALLS) $(WEBDIR)/ccan.tar.bz2 $(JUNKBALLS)
        DIR=`pwd` && cd /tmp && ln -sf "$$DIR" ccan && tar cvfj $@ `for f in $^; do echo ccan/$$f; done` ccan/junkcode && rm ccan
 
 $(ALL_PAGES): tools/doc_extract web/staticmoduleinfo.php
 
 $(WEBDIR)/list.html: web/staticall.php tools/doc_extract $(DIRECT_TARBALLS) $(DEPEND_TARBALLS) $(WEBDIR)/ccan.tar.bz2 $(JUNKBALLS)
-       $(PHP) web/staticall.php ccan/ junkcode/ $(WEBDIR) $(MODS) > $@
+       $(PHP) web/staticall.php ccan/ junkcode/ $(WEBDIR) ccan-$(VERSION_NUM).tar.bz2 $(MODS) > $@
 
 $(WEBDIR)/upload.html: web/staticupload.php
        $(PHP) web/staticupload.php > $@
 
 $(WEBDIR)/upload.html: web/staticupload.php
        $(PHP) web/staticupload.php > $@
@@ -57,7 +61,7 @@ $(WEBDIR)/uploader.php: web/uploader.php.cpp
        cpp -w -C -P $< | grep . > $@
 
 $(WEBDIR)/index.html: web/staticindex.php
        cpp -w -C -P $< | grep . > $@
 
 $(WEBDIR)/index.html: web/staticindex.php
-       $(PHP) web/staticindex.php > $@
+       $(PHP) web/staticindex.php ccan-$(VERSION_NUM).tar.bz2 > $@
 
 $(WEBDIR)/example-config.h: config.h
        cp $< $@
 
 $(WEBDIR)/example-config.h: config.h
        cp $< $@
index 1b36aa47e21c39c4d6b869b3aaaf791f27a55244..33201ab97a5d8745806c34e3074f57ff05e6eadc 100644 (file)
@@ -728,8 +728,6 @@ void *alloc_get(void *pool, unsigned long poolsize,
        bs = &head->bs[bucket];
 
        if (!bs->page_list) {
        bs = &head->bs[bucket];
 
        if (!bs->page_list) {
-               struct page_header *ph;
-
                if (large_page_bucket(bucket, sp_bits))
                        bs->page_list = get_large_page(head, poolsize,
                                                       sp_bits);
                if (large_page_bucket(bucket, sp_bits))
                        bs->page_list = get_large_page(head, poolsize,
                                                       sp_bits);
index ffd17c65734f881f1ca355ed9e1a5b705dd3ff46..d84974ee02cd3ce0f4c967c239e30e6b466f3241 100644 (file)
@@ -353,7 +353,8 @@ bool tiny_alloc_check(void *pool, unsigned long poolsize)
        unsigned long arrsize = free_array_size(poolsize);
        unsigned char *arr = pool;
        unsigned long len, off, hdrlen;
        unsigned long arrsize = free_array_size(poolsize);
        unsigned char *arr = pool;
        unsigned long len, off, hdrlen;
-       unsigned long i, freearr[arrsize], num_freearr = 0;
+       /* Don't have sanitizer complain here if arrsize is 0! */
+       unsigned long i, freearr[arrsize ? arrsize : 1], num_freearr = 0;
        bool free;
 
        if (poolsize < MIN_BLOCK_SIZE)
        bool free;
 
        if (poolsize < MIN_BLOCK_SIZE)
index a216f4781a504207097a01aacb937e6625d5f855..c28e0da2a496dcbdd09abf7688caf5bbc37e117d 100644 (file)
@@ -31,7 +31,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps,
        int8_t ret;
 
        ret = maps->decode_map[(unsigned char)b64letter];
        int8_t ret;
 
        ret = maps->decode_map[(unsigned char)b64letter];
-       if (ret == (char)0xff) {
+       if (ret == '\xff') {
                errno = EDOM;
                return -1;
        }
                errno = EDOM;
                return -1;
        }
@@ -41,7 +41,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps,
 
 bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char)
 {
 
 bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char)
 {
-       return (maps->decode_map[(const unsigned char)b64char] != (char)0xff);
+       return (maps->decode_map[(const unsigned char)b64char] != '\xff');
 }
 
 void base64_init_maps(base64_maps_t *dest, const char src[64])
 }
 
 void base64_init_maps(base64_maps_t *dest, const char src[64])
@@ -118,7 +118,7 @@ size_t base64_decoded_length(size_t srclen)
        return ((srclen+3)/4*3);
 }
 
        return ((srclen+3)/4*3);
 }
 
-int base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3],
+ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3],
                                     const char src[4])
 {
        signed char a;
                                     const char src[4])
 {
        signed char a;
@@ -143,7 +143,7 @@ int base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3],
 }
 
 
 }
 
 
-int base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3],
+ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3],
                                  const char * src, const size_t srclen)
 {
        char longsrc[4];
                                  const char * src, const size_t srclen)
 {
        char longsrc[4];
@@ -178,7 +178,7 @@ ssize_t base64_decode_using_maps(const base64_maps_t *maps,
 {
        ssize_t dest_offset = 0;
        ssize_t i;
 {
        ssize_t dest_offset = 0;
        ssize_t i;
-       size_t more;
+       ssize_t more;
 
        if (destlen < base64_decoded_length(srclen)) {
                errno = EOVERFLOW;
 
        if (destlen < base64_decoded_length(srclen)) {
                errno = EOVERFLOW;
index 405dc63fd44531d9bcfb55064ad3a0196f68f0dc..a899af4a357fc9718d5a1efd2d89b0d796aef291 100644 (file)
@@ -103,8 +103,8 @@ ssize_t base64_decode_using_maps(const base64_maps_t *maps,
  * @return Number of decoded bytes set in dest. -1 on error (and errno set)
  * @note sets errno = EDOM if src contains invalid characters
  */
  * @return Number of decoded bytes set in dest. -1 on error (and errno set)
  * @note sets errno = EDOM if src contains invalid characters
  */
-int base64_decode_quartet_using_maps(const base64_maps_t *maps,
-                                    char dest[3], const char src[4]);
+ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps,
+                                        char dest[3], const char src[4]);
 
 /**
  * base64_decode_tail_using_maps - decode the final bytes of a base64 string using a specific alphabet
 
 /**
  * base64_decode_tail_using_maps - decode the final bytes of a base64 string using a specific alphabet
@@ -116,8 +116,8 @@ int base64_decode_quartet_using_maps(const base64_maps_t *maps,
  * @note sets errno = EDOM if src contains invalid characters
  * @note sets errno = EINVAL if src is an invalid base64 tail
  */
  * @note sets errno = EDOM if src contains invalid characters
  * @note sets errno = EINVAL if src is an invalid base64 tail
  */
-int base64_decode_tail_using_maps(const base64_maps_t *maps, char *dest,
-                                 const char *src, size_t srclen);
+ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3],
+                                     const char *src, size_t srclen);
 
 
 /* the rfc4648 functions: */
 
 
 /* the rfc4648 functions: */
@@ -212,7 +212,7 @@ ssize_t base64_decode(char *dest, size_t destlen,
  * @note sets errno = EDOM if src contains invalid characters
  */
 static inline
  * @note sets errno = EDOM if src contains invalid characters
  */
 static inline
-int base64_decode_quartet(char dest[3], const char src[4])
+ssize_t base64_decode_quartet(char dest[3], const char src[4])
 {
        return base64_decode_quartet_using_maps(&base64_maps_rfc4648,
                                                dest, src);
 {
        return base64_decode_quartet_using_maps(&base64_maps_rfc4648,
                                                dest, src);
index 5dba932d4799af3b61134a0eb3ecd6d135e18b8e..6bb3acf5037173a328fa50b0c6b120dc310f8d82 100644 (file)
@@ -10,7 +10,7 @@ int main(void)
        plan_tests(68 + 6 * (31 + 63));
 
        for (i = 0; i < 32; i++)
        plan_tests(68 + 6 * (31 + 63));
 
        for (i = 0; i < 32; i++)
-               ok1(bitops_ffs32(1 << i) == i+1);
+               ok1(bitops_ffs32(1U << i) == i+1);
        ok1(bitops_ffs32(0) == 0);
        for (i = 0; i < 64; i++)
                ok1(bitops_ffs64((uint64_t)1 << i) == i+1);
        ok1(bitops_ffs32(0) == 0);
        for (i = 0; i < 64; i++)
                ok1(bitops_ffs64((uint64_t)1 << i) == i+1);
@@ -25,19 +25,19 @@ int main(void)
        ok1(bitops_ffs64(0) == 0);
 
        for (i = 0; i < 32; i++)
        ok1(bitops_ffs64(0) == 0);
 
        for (i = 0; i < 32; i++)
-               ok1(bitops_clz32(1 << i) == 31 - i);
+               ok1(bitops_clz32(1U << i) == 31 - i);
        for (i = 0; i < 64; i++)
                ok1(bitops_clz64((uint64_t)1 << i) == 63 - i);
 
        /* Lower bits don't effect results */
        for (i = 0; i < 32; i++)
        for (i = 0; i < 64; i++)
                ok1(bitops_clz64((uint64_t)1 << i) == 63 - i);
 
        /* Lower bits don't effect results */
        for (i = 0; i < 32; i++)
-               ok1(bitops_clz32((1 << i) + (1 << i)-1) == 31 - i);
+               ok1(bitops_clz32((1U << i) + (1U << i)-1) == 31 - i);
        for (i = 0; i < 64; i++)
                ok1(bitops_clz64(((uint64_t)1 << i) + ((uint64_t)1 << i)-1)
                    == 63 - i);
 
        for (i = 0; i < 32; i++)
        for (i = 0; i < 64; i++)
                ok1(bitops_clz64(((uint64_t)1 << i) + ((uint64_t)1 << i)-1)
                    == 63 - i);
 
        for (i = 0; i < 32; i++)
-               ok1(bitops_ctz32(1 << i) == i);
+               ok1(bitops_ctz32(1U << i) == i);
        for (i = 0; i < 64; i++)
                ok1(bitops_ctz64((uint64_t)1 << i) == i);
 
        for (i = 0; i < 64; i++)
                ok1(bitops_ctz64((uint64_t)1 << i) == i);
 
index bc99e7951c739e94221148e12491ea994642fbae..a0689db15b2f4457a9a15525edf08ee5ae4b55f2 100644 (file)
@@ -203,8 +203,13 @@ static inline const char *bytestring_rindex(struct bytestring haystack,
 static inline struct bytestring bytestring_bytestring(struct bytestring haystack,
                                                      struct bytestring needle)
 {
 static inline struct bytestring bytestring_bytestring(struct bytestring haystack,
                                                      struct bytestring needle)
 {
-       const char *p = memmem(haystack.ptr, haystack.len,
-                              needle.ptr, needle.len);
+       const char *p;
+
+       /* Allow needle.ptr == NULL, without memmem sanitizer complaining */
+       if (needle.len == 0)
+               return bytestring(haystack.ptr, 0);
+
+       p = memmem(haystack.ptr, haystack.len, needle.ptr, needle.len);
        if (p)
                return bytestring(p, needle.len);
        else
        if (p)
                return bytestring(p, needle.len);
        else
diff --git a/ccan/closefrom/LICENSE b/ccan/closefrom/LICENSE
new file mode 120000 (symlink)
index 0000000..b7951da
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/CC0
\ No newline at end of file
diff --git a/ccan/closefrom/_info b/ccan/closefrom/_info
new file mode 100644 (file)
index 0000000..4dc1d7f
--- /dev/null
@@ -0,0 +1,69 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * closefrom - close all fds starting from specified fd.
+ *
+ * This code is an example of what to do in a child process to
+ * ensure that none of the (possibly sensitive) file descriptors
+ * in the parent remain in the child process.
+ *
+ * License: CC0 (Public domain)
+ * Author: ZmnSCPxj jxPCSnmZ <ZmnSCPxj@protonmail.com>
+ *
+ * Example:
+ * #include <ccan/closefrom/closefrom.h>
+ * #include <ccan/err/err.h>
+ * #include <stdio.h>
+ * #include <sys/resource.h>
+ * #include <sys/time.h>
+ * #include <sys/types.h>
+ * #include <sys/wait.h>
+ * #include <unistd.h>
+ *
+ * int main(int argc, char **argv)
+ * {
+ *     pid_t child;
+ *
+ *     // If being emulated, then we might end up
+ *     // looping over a large _SC_OPEN_MAX
+ *     // (Some systems have it as INT_MAX!)
+ *     // If so, closefrom_limit will lower this limit
+ *     // to a value you specify, or if given 0 will
+ *     // limit to 4096.
+ *     // Call this as early as possible.
+ *     closefrom_limit(0);
+ *
+ *     // If we limited, we can query this so we can
+ *     // print it in debug logs or something.
+ *     if (closefrom_may_be_slow())
+ *             printf("we limited ourselves to 4096 fds.\n");
+ *
+ *     child = fork();
+ *     if (child < 0)
+ *             err(1, "Forking");
+ *     if (child == 0) {
+ *             closefrom(STDERR_FILENO + 1);
+ *             // Insert your *whatever* code here.
+ *             _exit(0);
+ *     }
+ *
+ *     waitpid(child, NULL, 0);
+ *
+ *     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/ccan/closefrom/closefrom.c b/ccan/closefrom/closefrom.c
new file mode 100644 (file)
index 0000000..177b05e
--- /dev/null
@@ -0,0 +1,225 @@
+/* CC0 license (public domain) - see LICENSE file for details */
+#include <ccan/closefrom/closefrom.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/* See also:
+ * https://stackoverflow.com/a/918469
+ *
+ * The implementation below is not exhaustive of all the suggested above.
+ */
+
+#if !HAVE_CLOSEFROM
+
+/* IBM AIX.
+ * https://www.ibm.com/docs/en/aix/7.2?topic=f-fcntl-dup-dup2-subroutine
+ */
+#if HAVE_F_CLOSEM
+
+#include <fcntl.h>
+
+void closefrom(int fromfd)
+{
+       (void) fcntl(fromfd, F_CLOSEM, 0);
+}
+
+bool closefrom_may_be_slow(void)
+{
+       return false;
+}
+
+#else /* !HAVE_F_CLOSEM */
+
+#if HAVE_NR_CLOSE_RANGE
+#include <sys/syscall.h>
+#endif
+
+#define PROC_PID_FD_LEN \
+       ( 6  /* /proc/ */ \
+       + 20 /* 64-bit $PID */ \
+       + 3  /* /fd */ \
+       + 1  /* NUL */ \
+       )
+
+static bool can_get_maxfd(void)
+{
+#if HAVE_F_MAXFD
+       int res = fcntl(0, F_MAXFD);
+       if (res < 0)
+               return false;
+       else
+               return true;
+#else
+       return false;
+#endif
+}
+
+/* Linux >= 5.9 */
+static bool can_close_range(void)
+{
+#if HAVE_NR_CLOSE_RANGE
+       int res = syscall(__NR_close_range, INT_MAX, INT_MAX, 0);
+       if (res < 0)
+               return false;
+       return true;
+#else
+       return false;
+#endif
+}
+
+/* On Linux, Solaris, AIX, Cygwin, and NetBSD.  */
+static bool can_open_proc_pid_fd(void)
+{
+       char dnam[PROC_PID_FD_LEN];
+       DIR *dir;
+
+       sprintf(dnam, "/proc/%ld/fd", (long) getpid());
+       dir = opendir(dnam);
+       if (!dir)
+               return false;
+       closedir(dir);
+       return true;
+}
+
+/* On FreeBSD and MacOS.  */
+static bool can_open_dev_fd(void)
+{
+       DIR *dir;
+       dir = opendir("/dev/fd");
+       if (!dir)
+               return false;
+       closedir(dir);
+       return true;
+}
+
+bool closefrom_may_be_slow(void)
+{
+       if (can_get_maxfd())
+               return false;
+       else if (can_close_range())
+               return false;
+       else if (can_open_proc_pid_fd())
+               return false;
+       else if (can_open_dev_fd())
+               return false;
+       else
+               return true;
+}
+
+/* It is possible that we run out of available file descriptors.
+ * However, if we are going to close anyway, we could just try
+ * closing file descriptors until we reach maxfd.
+ */
+static
+DIR *try_opendir(const char *dnam, int *fromfd, int maxfd)
+{
+       DIR *dir;
+
+       do {
+               dir = opendir(dnam);
+               if (!dir && (errno == ENFILE || errno == EMFILE)) {
+                       if (*fromfd < maxfd)
+                               close((*fromfd)++);
+                       else
+                               break;
+               }
+       } while (!dir && (errno == ENFILE || errno == EMFILE));
+
+       return dir;
+}
+
+void closefrom(int fromfd)
+{
+       int saved_errno = errno;
+
+       int res;
+       int maxfd;
+
+       char dnam[PROC_PID_FD_LEN];
+       DIR *dir;
+       struct dirent *entry;
+
+       (void) res;
+
+       if (fromfd < 0)
+               goto quit;
+
+#if HAVE_NR_CLOSE_RANGE
+       res = syscall(__NR_close_range, fromfd, INT_MAX, 0);
+       if (res == 0)
+               goto quit;
+#endif
+
+       maxfd = sysconf(_SC_OPEN_MAX);
+
+       sprintf(dnam, "/proc/%ld/fd", (long) getpid());
+       dir = try_opendir(dnam, &fromfd, maxfd);
+       if (!dir)
+               dir = try_opendir("/dev/fd", &fromfd, maxfd);
+
+       if (dir) {
+               while ((entry = readdir(dir))) {
+                       long fd;
+                       char *endp;
+
+                       fd = strtol(entry->d_name, &endp, 10);
+                       if (entry->d_name != endp && *endp == '\0' &&
+                           fd >= 0 && fd < INT_MAX && fd >= fromfd &&
+                           fd != dirfd(dir) )
+                               close(fd);
+               }
+               closedir(dir);
+               goto quit;
+       }
+
+#if HAVE_F_MAXFD
+       res = fcntl(0, F_MAXFD);
+       if (res >= 0)
+               maxfd = res + 1;
+#endif
+
+       /* Fallback.  */
+       for (; fromfd < maxfd; ++fromfd)
+               close(fromfd);
+
+quit:
+       errno = saved_errno;
+}
+
+#endif /* !HAVE_F_CLOSEM */
+
+void closefrom_limit(unsigned int arg_limit)
+{
+       rlim_t limit = (rlim_t) arg_limit;
+
+       struct rlimit nofile;
+
+       if (!closefrom_may_be_slow())
+               return;
+
+       if (limit == 0)
+               limit = 4096;
+
+       getrlimit(RLIMIT_NOFILE, &nofile);
+
+       /* Respect the max limit.
+        * If we are not running as root then we cannot raise
+        * it, but we *can* lower the max limit.
+        */
+       if (nofile.rlim_max != RLIM_INFINITY && limit > nofile.rlim_max)
+               limit = nofile.rlim_max;
+
+       nofile.rlim_cur = limit;
+       nofile.rlim_max = limit;
+
+       setrlimit(RLIMIT_NOFILE, &nofile);
+}
+
+#endif /* !HAVE_CLOSEFROM */
diff --git a/ccan/closefrom/closefrom.h b/ccan/closefrom/closefrom.h
new file mode 100644 (file)
index 0000000..790a5ba
--- /dev/null
@@ -0,0 +1,81 @@
+/* CC0 license (public domain) - see LICENSE file for details */
+#ifndef CCAN_CLOSEFROM_H
+#define CCAN_CLOSEFROM_H
+#include "config.h"
+#include <stdbool.h>
+
+#if HAVE_CLOSEFROM
+/* BSD.  */
+#include <unistd.h>
+/* Solaris.  */
+#include <stdlib.h>
+
+static inline
+bool closefrom_may_be_slow(void)
+{
+       return 0;
+}
+
+static inline
+void closefrom_limit(unsigned int limit)
+{
+}
+
+#else /* !HAVE_CLOSEFROM */
+
+/**
+ * closefrom - Close all open file descriptors, starting
+ * at fromfd onwards.
+ * @fromfd: the first fd to close; it and all higher file descriptors
+ * will be closed.
+ *
+ * This is not multithread-safe: other threads in the same process
+ * may or may not open new file descriptors in parallel to this call.
+ * However, the expected use-case is that this will be called in a
+ * child process just after fork(), meaning the child process is still
+ * single-threaded.
+ */
+void closefrom_(int fromfd);
+/* In case the standard library has it, but declared in some
+ * *other* header we do not know of yet, we use closefrom_ in
+ * the actual name the linker sees.
+ */
+#define closefrom closefrom_
+
+/**
+ * closefrom_may_be_slow - check if the closefrom() function could
+ * potentially take a long time.
+ *
+ * The return value is true if closefrom() is emulated by
+ * looping from fromfd to sysconf(_SC_OPEN_MAX), which can be
+ * very large (possibly even INT_MAX on some systems).
+ * If so, you might want to use setrlimit to limit _SC_OPEN_MAX.
+ * If this returns false, then closefrom is efficient and you do not
+ * need to limit the number of file descriptors.
+ *
+ * You can use closefrom_limit to perform the limiting based on
+ * closefrom_may_be_slow.
+ * This API is exposed in case you want to output to debug logs or
+ * something similar.
+ */
+bool closefrom_may_be_slow(void);
+
+/**
+ * closefrom_limit - If closefrom_may_be_slow(), lower the limit on
+ * the number of file descriptors we keep open, to prevent closefrom
+ * from being *too* slow.
+ * @limit: 0 to use a reasonable default of 4096, or non-zero for the
+ * limit you prefer.
+ *
+ * This function does nothing if closefrom_may_be_slow() return false.
+ *
+ * This function only *lowers* the limit from the hard limit set by
+ * root before running this program.
+ * If the limit is higher than the hard limit, then the hard limit is
+ * respected.
+ */
+void closefrom_limit(unsigned int limit);
+
+#endif /* !HAVE_CLOSEFROM */
+
+#endif /* CCAN_CLOSEFROM_H */
diff --git a/ccan/closefrom/test/run.c b/ccan/closefrom/test/run.c
new file mode 100644 (file)
index 0000000..10e4015
--- /dev/null
@@ -0,0 +1,192 @@
+#include <ccan/closefrom/closefrom.h>
+/* Include the C files directly. */
+#include <ccan/closefrom/closefrom.c>
+#include <ccan/tap/tap.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* Open a pipe, do closefrom, check pipe no longer works.   */
+static
+int pipe_close(void)
+{
+       int fds[2];
+       ssize_t wres;
+
+       char buf = '\0';
+
+       if (pipe(fds) < 0)
+               return 0;
+
+       /* Writing to the write end should succeed, the
+        * pipe is working.  */
+       do {
+               wres = write(fds[1], &buf, 1);
+       } while ((wres < 0) && (errno == EINTR));
+       if (wres < 0)
+               return 0;
+
+       closefrom(STDERR_FILENO + 1);
+
+       /* Writing to the write end should fail because
+        * everything should be closed.  */
+       do {
+               wres = write(fds[1], &buf, 1);
+       } while ((wres < 0) && (errno == EINTR));
+
+       return (wres < 0) && (errno == EBADF);
+}
+
+/* Open a pipe, fork, do closefrom in child, read pipe from parent,
+ * parent should see EOF.
+ */
+static
+int fork_close(void)
+{
+       int fds[2];
+       pid_t child;
+
+       char buf;
+       ssize_t rres;
+
+       if (pipe(fds) < 0)
+               return 0;
+
+       child = fork();
+       if (child < 0)
+               return 0;
+
+       if (child == 0) {
+               /* Child.  */
+               closefrom(STDERR_FILENO + 1);
+               _exit(0);
+       } else {
+               /* Parent.  */
+
+               /* Close write end of pipe.  */
+               close(fds[1]);
+
+               do {
+                       rres = read(fds[0], &buf, 1);
+               } while ((rres < 0) && (errno == EINTR));
+
+               /* Should have seen EOF.  */
+               if (rres != 0)
+                       return 0;
+
+               /* Clean up.  */
+               waitpid(child, NULL, 0);
+               closefrom(STDERR_FILENO + 1);
+       }
+
+       return 1;
+}
+/* Open a pipe, fork, in child set the write end to fd #3,
+ * in parent set the read end to fd #3, send a byte from
+ * child to parent, check.
+ */
+static
+int fork_communicate(void)
+{
+       int fds[2];
+       pid_t child;
+
+       char wbuf = 42;
+       char rbuf;
+       ssize_t rres;
+       ssize_t wres;
+
+       int status;
+
+       if (pipe(fds) < 0)
+               return 0;
+
+       child = fork();
+       if (child < 0)
+               return 0;
+
+       if (child == 0) {
+               /* Child.  */
+
+               /* Move write end to fd #3.  */
+               if (fds[1] != 3) {
+                       if (dup2(fds[1], 3) < 0)
+                               _exit(127);
+                       close(fds[1]);
+                       fds[1] = 3;
+               }
+
+               closefrom(4);
+
+               do {
+                       wres = write(fds[1], &wbuf, 1);
+               } while ((wres < 0) && (errno == EINTR));
+               if (wres < 0)
+                       _exit(127);
+
+               _exit(0);
+       } else {
+               /* Parent.  */
+
+               /* Move read end to fd #3.  */
+               if (fds[0] != 3) {
+                       if (dup2(fds[0], 3) < 0)
+                               return 0;
+                       close(fds[0]);
+                       fds[0] = 3;
+               }
+
+               closefrom(4);
+
+               /* Wait for child to finish.  */
+               waitpid(child, &status, 0);
+               if (!WIFEXITED(status))
+                       return 0;
+               if (WEXITSTATUS(status) != 0)
+                       return 0;
+
+               /* Read 1 byte.  */
+               do {
+                       rres = read(fds[0], &rbuf, 1);
+               } while ((rres < 0) && (errno == EINTR));
+               if (rres < 0)
+                       return 0;
+               if (rres != 1)
+                       return 0;
+               /* Should get same byte as what was sent.  */
+               if (rbuf != wbuf)
+                       return 0;
+
+               /* Next attempt to read should EOF.  */
+               do {
+                       rres = read(fds[0], &rbuf, 1);
+               } while ((rres < 0) && (errno == EINTR));
+               if (rres < 0)
+                       return 0;
+               /* Should EOF.  */
+               if (rres != 0)
+                       return 0;
+
+       }
+
+       /* Clean up.  */
+       close(fds[0]);
+       return 1;
+}
+
+int main(void)
+{
+       /* Limit closefrom.  */
+       closefrom_limit(0);
+
+       /* This is how many tests you plan to run */
+       plan_tests(3);
+
+       ok1(pipe_close());
+       ok1(fork_close());
+       ok1(fork_communicate());
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
index 1bbb3b8b61c6cb4a0fd44ab39e5bf7505eaa91bd..562b29ec71cc13969ab0d111477608cdbc9d9c36 100644 (file)
 #define NON_NULL_ARGS(...)
 #endif
 
 #define NON_NULL_ARGS(...)
 #endif
 
+#if HAVE_ATTRIBUTE_RETURNS_NONNULL
+/**
+ * RETURNS_NONNULL - specify that this function cannot return NULL.
+ *
+ * Mainly an optimization opportunity, but can also suppress warnings.
+ *
+ * Example:
+ * RETURNS_NONNULL char *my_copy(char *buf);
+ */
+#define RETURNS_NONNULL __attribute__((__returns_nonnull__))
+#else
+#define RETURNS_NONNULL
+#endif
 
 #if HAVE_ATTRIBUTE_SENTINEL
 /**
 
 #if HAVE_ATTRIBUTE_SENTINEL
 /**
diff --git a/ccan/crc32c/LICENSE b/ccan/crc32c/LICENSE
new file mode 120000 (symlink)
index 0000000..2354d12
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
index 9eaec79b03f70fc5c76d792a8edd91a7b370f6b1..0203a22bf7e87886aedcdebd25fad3dfa06c9b8e 100644 (file)
@@ -72,7 +72,8 @@ static inline uint32_t gf2_matrix_times(uint32_t *mat, uint32_t vec) {
 /* Multiply a matrix by itself over GF(2).  Both mat and square must have 32
    rows. */
 static inline void gf2_matrix_square(uint32_t *square, uint32_t *mat) {
 /* Multiply a matrix by itself over GF(2).  Both mat and square must have 32
    rows. */
 static inline void gf2_matrix_square(uint32_t *square, uint32_t *mat) {
-    for (unsigned n = 0; n < 32; n++)
+    unsigned n;
+    for (n = 0; n < 32; n++)
         square[n] = gf2_matrix_times(mat, mat[n]);
 }
 
         square[n] = gf2_matrix_times(mat, mat[n]);
 }
 
@@ -87,7 +88,8 @@ static void crc32c_zeros_op(uint32_t *even, size_t len) {
     /* put operator for one zero bit in odd */
     odd[0] = POLY;              /* CRC-32C polynomial */
     uint32_t row = 1;
     /* put operator for one zero bit in odd */
     odd[0] = POLY;              /* CRC-32C polynomial */
     uint32_t row = 1;
-    for (unsigned n = 1; n < 32; n++) {
+    unsigned n;
+    for (n = 1; n < 32; n++) {
         odd[n] = row;
         row <<= 1;
     }
         odd[n] = row;
         row <<= 1;
     }
@@ -111,7 +113,7 @@ static void crc32c_zeros_op(uint32_t *even, size_t len) {
     } while (len);
 
     /* answer ended up in odd -- copy to even */
     } while (len);
 
     /* answer ended up in odd -- copy to even */
-    for (unsigned n = 0; n < 32; n++)
+    for (n = 0; n < 32; n++)
         even[n] = odd[n];
 }
 
         even[n] = odd[n];
 }
 
@@ -121,7 +123,8 @@ static void crc32c_zeros(uint32_t zeros[][256], size_t len) {
     uint32_t op[32];
 
     crc32c_zeros_op(op, len);
     uint32_t op[32];
 
     crc32c_zeros_op(op, len);
-    for (unsigned n = 0; n < 256; n++) {
+    unsigned n;
+    for (n = 0; n < 256; n++) {
         zeros[0][n] = gf2_matrix_times(op, n);
         zeros[1][n] = gf2_matrix_times(op, n << 8);
         zeros[2][n] = gf2_matrix_times(op, n << 16);
         zeros[0][n] = gf2_matrix_times(op, n);
         zeros[1][n] = gf2_matrix_times(op, n << 8);
         zeros[2][n] = gf2_matrix_times(op, n << 16);
@@ -265,7 +268,8 @@ uint32_t crc32c(uint32_t crc, void const *buf, size_t len) {
 static bool crc32c_once_little;
 static uint32_t crc32c_table_little[8][256];
 static void crc32c_init_sw_little(void) {
 static bool crc32c_once_little;
 static uint32_t crc32c_table_little[8][256];
 static void crc32c_init_sw_little(void) {
-    for (unsigned n = 0; n < 256; n++) {
+    unsigned n;
+    for (n = 0; n < 256; n++) {
         uint32_t crc = n;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         uint32_t crc = n;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
@@ -277,9 +281,10 @@ static void crc32c_init_sw_little(void) {
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc32c_table_little[0][n] = crc;
     }
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc32c_table_little[0][n] = crc;
     }
-    for (unsigned n = 0; n < 256; n++) {
+    for (n = 0; n < 256; n++) {
         uint32_t crc = crc32c_table_little[0][n];
         uint32_t crc = crc32c_table_little[0][n];
-        for (unsigned k = 1; k < 8; k++) {
+        unsigned k;
+        for (k = 1; k < 8; k++) {
             crc = crc32c_table_little[0][crc & 0xff] ^ (crc >> 8);
             crc32c_table_little[k][n] = crc;
         }
             crc = crc32c_table_little[0][crc & 0xff] ^ (crc >> 8);
             crc32c_table_little[k][n] = crc;
         }
@@ -340,7 +345,8 @@ static bool crc32c_once_big;
 static uint32_t crc32c_table_big_byte[256];
 static uint64_t crc32c_table_big[8][256];
 static void crc32c_init_sw_big(void) {
 static uint32_t crc32c_table_big_byte[256];
 static uint64_t crc32c_table_big[8][256];
 static void crc32c_init_sw_big(void) {
-    for (unsigned n = 0; n < 256; n++) {
+    unsigned n;
+    for (n = 0; n < 256; n++) {
         uint32_t crc = n;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         uint32_t crc = n;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
@@ -352,10 +358,11 @@ static void crc32c_init_sw_big(void) {
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc32c_table_big_byte[n] = crc;
     }
         crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
         crc32c_table_big_byte[n] = crc;
     }
-    for (unsigned n = 0; n < 256; n++) {
+    for (n = 0; n < 256; n++) {
         uint32_t crc = crc32c_table_big_byte[n];
         crc32c_table_big[0][n] = swap(crc);
         uint32_t crc = crc32c_table_big_byte[n];
         crc32c_table_big[0][n] = swap(crc);
-        for (unsigned k = 1; k < 8; k++) {
+        unsigned k;
+        for (k = 1; k < 8; k++) {
             crc = crc32c_table_big_byte[crc & 0xff] ^ (crc >> 8);
             crc32c_table_big[k][n] = swap(crc);
         }
             crc = crc32c_table_big_byte[crc & 0xff] ^ (crc >> 8);
             crc32c_table_big[k][n] = swap(crc);
         }
index 0392afe5c1127134e61e48d864dffe94252aeaa8..2238f9dc8fffbd4ee049ec8c965e0c11aeef37e2 100644 (file)
@@ -35,7 +35,8 @@ void hmac_sha256_init(struct hmac_sha256_ctx *ctx,
         *  (e.g., if K is of length 20 bytes and B=64, then K will be
         *   appended with 44 zero bytes 0x00)
         */
         *  (e.g., if K is of length 20 bytes and B=64, then K will be
         *   appended with 44 zero bytes 0x00)
         */
-       memcpy(k_ipad, k, ksize);
+       if (ksize != 0)
+               memcpy(k_ipad, k, ksize);
        memset((char *)k_ipad + ksize, 0, HMAC_SHA256_BLOCKSIZE - ksize);
 
        /*
        memset((char *)k_ipad + ksize, 0, HMAC_SHA256_BLOCKSIZE - ksize);
 
        /*
diff --git a/ccan/crypto/xtea/LICENSE b/ccan/crypto/xtea/LICENSE
new file mode 120000 (symlink)
index 0000000..08d5d48
--- /dev/null
@@ -0,0 +1 @@
+../../../licenses/CC0
\ No newline at end of file
index 28435d6a97c09588e8642ba3f29efdfb304599b2..43ce55733e7fc4ae4042515720bd6c00c7f2df19 100644 (file)
@@ -11,8 +11,9 @@ uint64_t xtea_encipher(const struct xtea_secret *secret, uint64_t v)
 {
        const uint32_t delta=0x9E3779B9;
        uint32_t v0=(v>>32), v1=v, sum=0;
 {
        const uint32_t delta=0x9E3779B9;
        uint32_t v0=(v>>32), v1=v, sum=0;
+       int i;
 
 
-       for (int i=0; i < NUM_DOUBLE_ROUNDS; i++) {
+       for (i=0; i < NUM_DOUBLE_ROUNDS; i++) {
                v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + secret->u.u32[sum & 3]);
                sum += delta;
                v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + secret->u.u32[(sum>>11) & 3]);
                v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + secret->u.u32[sum & 3]);
                sum += delta;
                v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + secret->u.u32[(sum>>11) & 3]);
@@ -24,8 +25,9 @@ uint64_t xtea_decipher(const struct xtea_secret *secret, uint64_t e)
 {
        const uint32_t delta=0x9E3779B9;
        uint32_t v0=(e>>32), v1=e, sum=delta*NUM_DOUBLE_ROUNDS;
 {
        const uint32_t delta=0x9E3779B9;
        uint32_t v0=(e>>32), v1=e, sum=delta*NUM_DOUBLE_ROUNDS;
+       int i;
 
 
-       for (int i=0; i < NUM_DOUBLE_ROUNDS; i++) {
+       for (i=0; i < NUM_DOUBLE_ROUNDS; i++) {
                v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + secret->u.u32[(sum>>11) & 3]);
                sum -= delta;
                v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + secret->u.u32[sum & 3]);
                v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + secret->u.u32[(sum>>11) & 3]);
                sum -= delta;
                v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + secret->u.u32[sum & 3]);
index bd32ecbbaeebc866426eb212ca3811264e060d21..ff5807240ae67a4417403c6118db11ca190782b6 100644 (file)
@@ -6,7 +6,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
 #include <sys/stat.h>
 #include <fcntl.h>
 
-/* This code is based on Stevens Advanced Programming in the UNIX
+/* This code is based on Stevens' Advanced Programming in the UNIX
  * Environment. */
 bool daemonize(void)
 {
  * Environment. */
 bool daemonize(void)
 {
index 58470fdee6c5561e49c265b1a975fa90b6b9ca26..0b98fdacddb4febe6d0d92bb5d07bfd4a454fd62 100644 (file)
@@ -183,15 +183,21 @@ typedef darray(unsigned long)  darray_ulong;
 
 #define darray_append_items(arr, items, count) do { \
                size_t count_ = (count), oldSize_ = (arr).size; \
 
 #define darray_append_items(arr, items, count) do { \
                size_t count_ = (count), oldSize_ = (arr).size; \
-               darray_resize(arr, oldSize_ + count_); \
-               memcpy((arr).item + oldSize_, items, count_ * sizeof(*(arr).item)); \
+               /* Don't memcpy NULL! */                        \
+               if (count_) {                                   \
+                       darray_resize(arr, oldSize_ + count_);          \
+                       memcpy((arr).item + oldSize_, items, count_ * sizeof(*(arr).item)); \
+               }                                                       \
        } while(0)
 
 #define darray_prepend_items(arr, items, count) do { \
                size_t count_ = (count), oldSize_ = (arr).size; \
                darray_resize(arr, count_ + oldSize_); \
        } while(0)
 
 #define darray_prepend_items(arr, items, count) do { \
                size_t count_ = (count), oldSize_ = (arr).size; \
                darray_resize(arr, count_ + oldSize_); \
-               memmove((arr).item + count_, (arr).item, oldSize_ * sizeof(*(arr).item)); \
-               memcpy((arr).item, items, count_ * sizeof(*(arr).item)); \
+               /* Don't memcpy NULL! */                        \
+               if (count_) {                                   \
+                       memmove((arr).item + count_, (arr).item, oldSize_ * sizeof(*(arr).item)); \
+                       memcpy((arr).item, items, count_ * sizeof(*(arr).item)); \
+               }                                                       \
        } while(0)
 
 #define darray_append_items_nullterminate(arr, items, count) do { \
        } while(0)
 
 #define darray_append_items_nullterminate(arr, items, count) do { \
index 695f50d3996bc188a29d869663a3262a781762e3..f3f0cb169756c4d16e452229c559c6e5f0cdaf68 100644 (file)
@@ -41,10 +41,11 @@ ed_dist edit_distance_dl(const ed_elem *src, ed_size slen,
        ed_dist *delcost = malloc(ED_TMAT_SIZE(slen + 1) * sizeof(ed_dist));
        ed_dist *delcostitr = delcost;
        ed_dist *delcostprevitr = delcost;
        ed_dist *delcost = malloc(ED_TMAT_SIZE(slen + 1) * sizeof(ed_dist));
        ed_dist *delcostitr = delcost;
        ed_dist *delcostprevitr = delcost;
+       ed_size i2, i1;
        *delcostitr++ = 0;
        *delcostitr++ = 0;
-       for (ed_size i2 = 1; i2 <= slen; ++i2) {
+       for (i2 = 1; i2 <= slen; ++i2) {
                ed_dist costi2 = ED_DEL_COST(src[i2 - 1]);
                ed_dist costi2 = ED_DEL_COST(src[i2 - 1]);
-               for (ed_size i1 = 0; i1 < i2; ++i1) {
+               for (i1 = 0; i1 < i2; ++i1) {
                        *delcostitr++ = *delcostprevitr++ + costi2;
                }
                *delcostitr++ = 0;
                        *delcostitr++ = *delcostprevitr++ + costi2;
                }
                *delcostitr++ = 0;
@@ -61,10 +62,11 @@ ed_dist edit_distance_dl(const ed_elem *src, ed_size slen,
        ed_dist *inscost = malloc(ED_TMAT_SIZE(tlen + 1) * sizeof(ed_dist));
        ed_dist *inscostitr = inscost;
        ed_dist *inscostprevitr = inscost;
        ed_dist *inscost = malloc(ED_TMAT_SIZE(tlen + 1) * sizeof(ed_dist));
        ed_dist *inscostitr = inscost;
        ed_dist *inscostprevitr = inscost;
+       ed_size j2, j1;
        *inscostitr++ = 0;
        *inscostitr++ = 0;
-       for (ed_size j2 = 1; j2 <= tlen; ++j2) {
+       for (j2 = 1; j2 <= tlen; ++j2) {
                ed_dist costj2 = ED_INS_COST(tgt[j2 - 1]);
                ed_dist costj2 = ED_INS_COST(tgt[j2 - 1]);
-               for (ed_size j1 = 0; j1 < j2; ++j1) {
+               for (j1 = 0; j1 < j2; ++j1) {
                        *inscostitr++ = *inscostprevitr++ + costj2;
                }
                *inscostitr++ = 0;
                        *inscostitr++ = *inscostprevitr++ + costj2;
                }
                *inscostitr++ = 0;
@@ -73,7 +75,8 @@ ed_dist edit_distance_dl(const ed_elem *src, ed_size slen,
 #endif
 
        /* Initialize first row with maximal cost */
 #endif
 
        /* Initialize first row with maximal cost */
-       for (ed_size i = 0; i < slen + 2; ++i) {
+       ed_size i, j;
+       for (i = 0; i < slen + 2; ++i) {
                dist[i] = maxdist;
        }
 
                dist[i] = maxdist;
        }
 
@@ -83,11 +86,11 @@ ed_dist edit_distance_dl(const ed_elem *src, ed_size slen,
        /* Initialize row with cost to delete src[0..i-1] */
        dist[-1] = maxdist;
        dist[0] = 0;
        /* Initialize row with cost to delete src[0..i-1] */
        dist[-1] = maxdist;
        dist[0] = 0;
-       for (ed_size i = 1; i <= slen; ++i) {
+       for (i = 1; i <= slen; ++i) {
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
-       for (ed_size j = 1; j <= tlen; ++j) {
+       for (j = 1; j <= tlen; ++j) {
                /* Largest y < i such that src[y] = tgt[j] */
                ed_size lastsrc = 0;
                ed_dist *prevdist = dist;
                /* Largest y < i such that src[y] = tgt[j] */
                ed_size lastsrc = 0;
                ed_dist *prevdist = dist;
@@ -101,7 +104,8 @@ ed_dist edit_distance_dl(const ed_elem *src, ed_size slen,
                 * Loop invariant: lasttgt[ED_HASH_ELEM(c)] holds the largest
                 * x < j such that tgt[x-1] = c or 0 if no such x exists.
                 */
                 * Loop invariant: lasttgt[ED_HASH_ELEM(c)] holds the largest
                 * x < j such that tgt[x-1] = c or 0 if no such x exists.
                 */
-               for (ed_size i = 1; i <= slen; ++i) {
+               ed_size i;
+               for (i = 1; i <= slen; ++i) {
                        ed_size i1 = lastsrc;
                        ed_size j1 = lasttgt[ED_HASH_ELEM(src[i - 1])];
 
                        ed_size i1 = lastsrc;
                        ed_size j1 = lasttgt[ED_HASH_ELEM(src[i - 1])];
 
index 4bc867e6716fe3f278fc63f254ce65c1397909a3..190009c74f39d4b68c4fbe1ae37c756473ecc996 100644 (file)
@@ -13,6 +13,8 @@
 ed_dist edit_distance_lcs(const ed_elem *src, ed_size slen,
                          const ed_elem *tgt, ed_size tlen)
 {
 ed_dist edit_distance_lcs(const ed_elem *src, ed_size slen,
                          const ed_elem *tgt, ed_size tlen)
 {
+       ed_size i, j;
+
        /* Optimization: Avoid malloc when row of distance matrix can fit on
         * the stack.
         */
        /* Optimization: Avoid malloc when row of distance matrix can fit on
         * the stack.
         */
@@ -24,11 +26,11 @@ ed_dist edit_distance_lcs(const ed_elem *src, ed_size slen,
 
        /* Initialize row with cost to delete src[0..i-1] */
        dist[0] = 0;
 
        /* Initialize row with cost to delete src[0..i-1] */
        dist[0] = 0;
-       for (ed_size i = 1; i <= slen; ++i) {
+       for (i = 1; i <= slen; ++i) {
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
-       for (ed_size j = 1; j <= tlen; ++j) {
+       for (j = 1; j <= tlen; ++j) {
                /* Value for dist[j-1][i-1] (one row up, one col left). */
                ed_dist diagdist = dist[0];
                dist[0] = dist[0] + ED_INS_COST(tgt[j - 1]);
                /* Value for dist[j-1][i-1] (one row up, one col left). */
                ed_dist diagdist = dist[0];
                dist[0] = dist[0] + ED_INS_COST(tgt[j - 1]);
@@ -36,7 +38,7 @@ ed_dist edit_distance_lcs(const ed_elem *src, ed_size slen,
                /* Loop invariant: dist[i] is the edit distance between first j
                 * elements of tgt and first i elements of src.
                 */
                /* Loop invariant: dist[i] is the edit distance between first j
                 * elements of tgt and first i elements of src.
                 */
-               for (ed_size i = 1; i <= slen; ++i) {
+               for (i = 1; i <= slen; ++i) {
                        ed_dist nextdiagdist = dist[i];
 
                        if (ED_ELEM_EQUAL(src[i - 1], tgt[j - 1])) {
                        ed_dist nextdiagdist = dist[i];
 
                        if (ED_ELEM_EQUAL(src[i - 1], tgt[j - 1])) {
index fefa9da89885d9bc566fdd56130c467c28bb92b2..352dbd7876ed9b7a49c62dbbc0b60746133c385f 100644 (file)
@@ -13,6 +13,8 @@
 ed_dist edit_distance_lev(const ed_elem *src, ed_size slen,
                          const ed_elem *tgt, ed_size tlen)
 {
 ed_dist edit_distance_lev(const ed_elem *src, ed_size slen,
                          const ed_elem *tgt, ed_size tlen)
 {
+       ed_size i, j;
+
        /* Optimization: Avoid malloc when row of distance matrix can fit on
         * the stack.
         */
        /* Optimization: Avoid malloc when row of distance matrix can fit on
         * the stack.
         */
@@ -24,11 +26,11 @@ ed_dist edit_distance_lev(const ed_elem *src, ed_size slen,
 
        /* Initialize row with cost to delete src[0..i-1] */
        dist[0] = 0;
 
        /* Initialize row with cost to delete src[0..i-1] */
        dist[0] = 0;
-       for (ed_size i = 1; i <= slen; ++i) {
+       for (i = 1; i <= slen; ++i) {
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
-       for (ed_size j = 1; j <= tlen; ++j) {
+       for (j = 1; j <= tlen; ++j) {
                /* Value for dist[j-1][i-1] (one row up, one col left). */
                ed_dist diagdist = dist[0];
                dist[0] = dist[0] + ED_INS_COST(tgt[j - 1]);
                /* Value for dist[j-1][i-1] (one row up, one col left). */
                ed_dist diagdist = dist[0];
                dist[0] = dist[0] + ED_INS_COST(tgt[j - 1]);
@@ -36,7 +38,7 @@ ed_dist edit_distance_lev(const ed_elem *src, ed_size slen,
                /* Loop invariant: dist[i] is the edit distance between first j
                 * elements of tgt and first i elements of src.
                 */
                /* Loop invariant: dist[i] is the edit distance between first j
                 * elements of tgt and first i elements of src.
                 */
-               for (ed_size i = 1; i <= slen; ++i) {
+               for (i = 1; i <= slen; ++i) {
                        ed_dist nextdiagdist = dist[i];
 
                        if (ED_ELEM_EQUAL(src[i - 1], tgt[j - 1])) {
                        ed_dist nextdiagdist = dist[i];
 
                        if (ED_ELEM_EQUAL(src[i - 1], tgt[j - 1])) {
index 4f098cbe57242b29d08a221e2f80d795980eb3e5..9041448402e6b3a232b1c0586a79e10608bf79d6 100644 (file)
@@ -13,6 +13,8 @@
 ed_dist edit_distance_rdl(const ed_elem *src, ed_size slen,
                          const ed_elem *tgt, ed_size tlen)
 {
 ed_dist edit_distance_rdl(const ed_elem *src, ed_size slen,
                          const ed_elem *tgt, ed_size tlen)
 {
+       ed_size i, j;
+
        /* Optimization: Avoid malloc when required rows of distance matrix can
         * fit on the stack.
         */
        /* Optimization: Avoid malloc when required rows of distance matrix can
         * fit on the stack.
         */
@@ -32,11 +34,11 @@ ed_dist edit_distance_rdl(const ed_elem *src, ed_size slen,
 
        /* Initialize row with cost to delete src[0..i-1] */
        dist[0] = 0;
 
        /* Initialize row with cost to delete src[0..i-1] */
        dist[0] = 0;
-       for (ed_size i = 1; i <= slen; ++i) {
+       for (i = 1; i <= slen; ++i) {
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
                dist[i] = dist[i - 1] + ED_DEL_COST(src[i - 1]);
        }
 
-       for (ed_size j = 1; j <= tlen; ++j) {
+       for (j = 1; j <= tlen; ++j) {
                /* Value for dist[j-2][i-1] (two rows up, one col left). */
                /* Note: dist[0] is not initialized when j == 1, var unused. */
                ed_dist diagdist1 = prevdist[0];
                /* Value for dist[j-2][i-1] (two rows up, one col left). */
                /* Note: dist[0] is not initialized when j == 1, var unused. */
                ed_dist diagdist1 = prevdist[0];
@@ -51,7 +53,7 @@ ed_dist edit_distance_rdl(const ed_elem *src, ed_size slen,
                /* Loop invariant: dist[i] is the edit distance between first j
                 * elements of tgt and first i elements of src.
                 */
                /* Loop invariant: dist[i] is the edit distance between first j
                 * elements of tgt and first i elements of src.
                 */
-               for (ed_size i = 1; i <= slen; ++i) {
+               for (i = 1; i <= slen; ++i) {
                        ed_dist nextdiagdist = dist[i];
 
                        if (ED_ELEM_EQUAL(src[i - 1], tgt[j - 1])) {
                        ed_dist nextdiagdist = dist[i];
 
                        if (ED_ELEM_EQUAL(src[i - 1], tgt[j - 1])) {
index c61ce442a5d03b39444a87a468514f23c6f3c962..205ded254709e6611d67b4465ca3967744306252 100644 (file)
@@ -179,7 +179,8 @@ static struct failtest_call *add_history_(enum failtest_call_type type,
        call->line = line;
        call->cleanup = NULL;
        call->backtrace = get_backtrace(&call->backtrace_num);
        call->line = line;
        call->cleanup = NULL;
        call->backtrace = get_backtrace(&call->backtrace_num);
-       memcpy(&call->u, elem, elem_size);
+       if (elem_size != 0)
+               memcpy(&call->u, elem, elem_size);
        tlist_add_tail(&history, call, list);
        return call;
 }
        tlist_add_tail(&history, call, list);
        return call;
 }
@@ -1202,9 +1203,8 @@ static void cleanup_pipe(struct pipe_call *call, bool restore)
 int failtest_pipe(int pipefd[2], const char *file, unsigned line)
 {
        struct failtest_call *p;
 int failtest_pipe(int pipefd[2], const char *file, unsigned line)
 {
        struct failtest_call *p;
-       struct pipe_call call;
 
 
-       p = add_history(FAILTEST_PIPE, true, file, line, &call);
+       p = add_history_(FAILTEST_PIPE, true, file, line, NULL, 0);
        if (should_fail(p)) {
                p->u.open.ret = -1;
                /* FIXME: Play with error codes? */
        if (should_fail(p)) {
                p->u.open.ret = -1;
                /* FIXME: Play with error codes? */
diff --git a/ccan/graphql/LICENSE b/ccan/graphql/LICENSE
new file mode 120000 (symlink)
index 0000000..2354d12
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/graphql/_info b/ccan/graphql/_info
new file mode 100644 (file)
index 0000000..2920241
--- /dev/null
@@ -0,0 +1,66 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * graphql - Routines to lex and parse GraphQL.
+ *
+ * This code contains routines to lex and parse GraphQL code.
+ * This code was written per the spec at:
+ *    https://spec.graphql.org/draft/
+ * ...dated Fri, May 21, 2021 at the time of writing.
+ *    Copyright (c) 2021 WhiteCloudFarm.org <robert.lee.dickinson@gmail.com>
+ *    <https://github.com/rl-d/ccan>
+ * 
+ * Example:
+ *     
+ *     int main(int argc, char *argv[]) {
+ *     
+ *             const char *input_string = "{ fieldName }";
+ *             struct list_head *output_tokens;
+ *             struct graphql_executable_document *output_document;
+ *     
+ *             const char *errmsg = graphql_lexparse(
+ *                     NULL, // tal context
+ *                     input_string,
+ *                     &output_tokens, // variable to receive tokens
+ *                     &output_document); // variable to receive AST
+ *     
+ *             if (errmsg) {
+ *                     struct graphql_token *last_token;
+ *                     last_token = list_tail(output_tokens, struct graphql_token, node);
+ *                     printf("Line %d, col %d: %s",
+ *                             last_token->source_line,
+ *                             last_token->source_column + last_token->source_len,
+ *                             errmsg);
+ *             } else {
+ *                     // Normally you would check every indirection in the resulting AST for null
+ *                     // pointers, but for simplicity of example:
+ *                     printf("A field from the parsed string: %s\n",
+ *                             output_document->first_def->op_def->sel_set->
+ *                             first->field->name->token_string);
+ *             }
+ *     
+ *             output_tokens = tal_free(output_tokens);
+ *     }
+ *
+ * License: BSD-MIT
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/list\n");
+               printf("ccan/str\n");
+               printf("ccan/tal\n");
+               printf("ccan/tal/str\n");
+               printf("ccan/utf8\n");
+               return 0;
+       }
+
+       return 1;
+}
+
diff --git a/ccan/graphql/graphql.c b/ccan/graphql/graphql.c
new file mode 100644 (file)
index 0000000..640a76b
--- /dev/null
@@ -0,0 +1,968 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include "graphql.h"
+
+#include "ccan/tal/str/str.h"
+#include "ccan/utf8/utf8.h"
+
+
+/* GraphQL character classes
+ *
+ * These definitions are meant to reflect the GraphQL specification as
+ * literally as possible.
+ */
+#define SOURCE_CHAR(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || ((c) >= 32 && (c) <= 65535))
+#define WHITE_SPACE(c) ((c) == '\t' || (c) == ' ')
+#define LINE_TERMINATOR(c) ((c) == '\n' || (c) == '\r')
+#define COMMENT(c) ((c) == '#')
+#define COMMENT_CHAR(c) (SOURCE_CHAR(c) && !LINE_TERMINATOR(c))
+#define STRING_CHAR(c) (SOURCE_CHAR(c) && !LINE_TERMINATOR(c) && (c)!='"' && (c)!='\\')
+#define BLOCK_STRING_CHAR(c) (SOURCE_CHAR(c))
+#define COMMA(c) ((c) == ',')
+#define EOF_CHAR(c) ((c) == 0 || (c) == 4)
+#define PUNCTUATOR(c) (strchr("!$&().:=@[]{|}", c))
+#define HEX_DIGIT(c) (DIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
+#define DIGIT(c) ((c) >= '0' && (c) <= '9')
+#define NAME_START(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || (c) == '_')
+#define NAME_CONTINUE(c) (NAME_START(c) || DIGIT(c))
+
+// Helper for copying an overlapping string, since strcpy() is not safe for that
+#define cpystr(d,s) { char *cpystr_p; char *cpystr_q; for(cpystr_p = (s), cpystr_q = (d); *cpystr_p;) *cpystr_q++ = *cpystr_p++; *cpystr_q++ = *cpystr_p++; }
+
+/* Parser shorthands
+ *
+ * These shorthands are motivated by the parser functions, so they can be
+ * written in a format that corresponds closely to the specification.
+ */
+#define RET static void *
+#define PARAMS struct list_head *tokens, struct list_head *used, const char **err
+#define ARGS tokens, used, err
+#define INIT(type) \
+       struct graphql_token *rollback_top = list_top(tokens, struct graphql_token, node); \
+       struct graphql_##type *obj = talz(tokens, struct graphql_##type); \
+       (void)rollback_top; /* avoids unused variable warning */ \
+
+#define EXIT \
+       goto exit_label; /* avoids unused label warning */ \
+       exit_label: \
+       if (*err) obj = tal_free(obj); \
+       return obj; \
+
+#define CONSUME_ONE list_add(used, &list_pop(tokens, struct graphql_token, node)->node);
+#define RESTORE_ONE list_add(tokens, &list_pop(used, struct graphql_token, node)->node);
+#define ROLLBACK(args) while (list_top(tokens, struct graphql_token, node) != rollback_top) { RESTORE_ONE; }
+#define OR if (!*err) goto exit_label; *err = NULL;
+#define REQ if (*err) { ROLLBACK(args); goto exit_label; }
+#define OPT *err = NULL;
+#define WHILE_OPT while(!*err); *err = NULL;
+#define LOOKAHEAD(args, tok) struct graphql_token *tok = list_top(tokens, struct graphql_token, node);
+#define MSG(msg) if (*err) *err = msg;
+
+
+/* The following parser functions are written in a way that corresponds to the
+ * grammar defined in the GraphQL specification. The code is not intended to
+ * look like normal C code; it's designed for parsing clarity rather than C
+ * style. Think of it as something generated rather than something to read.
+ * For that reason, the functions follow special rules:
+ *
+ *     - The declaration is standardized with RET and PARAMS
+ *     - The "err" argument is assumed to be NULL upon entrance
+ *     - The "err" argument is set on failure
+ *     - If the function fails to parse, then "tokens" shall be as it was upon entrance
+ *     - INIT and EXIT macros are used
+ *     - Macros such as REQ and OPT facilitate readability and conciseness
+ */
+
+/* The following functions construct the "leaves" of the abstract syntax tree. */
+
+RET parse_keyword(PARAMS, const char *keyword, const char *errmsg) {
+       struct graphql_token *tok = list_top(tokens, struct graphql_token, node);
+       if (!tok || tok->token_type != 'a') {
+               *err = errmsg; return NULL;
+       }
+       if (!streq(tok->token_string, keyword)) {
+               *err = errmsg; return NULL;
+       }
+       CONSUME_ONE;
+       return tok;
+}
+
+// Note: a static buffer is used here.
+RET parse_punct(PARAMS, int punct) {
+       static char punctbuf[16];
+       struct graphql_token *tok = list_top(tokens, struct graphql_token, node);
+       if (!tok || tok->token_type != punct) {
+               if (punct == PUNCT_SPREAD)
+                       sprintf(punctbuf, "expected: '...'");
+               else
+                       sprintf(punctbuf, "expected: '%c'", punct);
+               *err = punctbuf; return NULL;
+       }
+       CONSUME_ONE;
+       return tok;
+}
+
+RET parse_name(PARAMS) {
+       struct graphql_token *tok = list_top(tokens, struct graphql_token, node);
+       if (!tok || tok->token_type != 'a') {
+               *err = "name expected"; return NULL;
+       }
+       CONSUME_ONE;
+       return tok;
+}
+
+RET parse_int(PARAMS) {
+       struct graphql_token *tok = list_top(tokens, struct graphql_token, node);
+       if (!tok || tok->token_type != 'i') {
+               *err = "integer expected"; return NULL;
+       }
+       CONSUME_ONE;
+       return tok;
+}
+
+RET parse_float(PARAMS) {
+       struct graphql_token *tok = list_top(tokens, struct graphql_token, node);
+       if (!tok || tok->token_type != 'f') {
+               *err = "float expected"; return NULL;
+       }
+       CONSUME_ONE;
+       return tok;
+}
+
+RET parse_string(PARAMS) {
+       struct graphql_token *tok = list_top(tokens, struct graphql_token, node);
+       if (!tok || tok->token_type != 's') {
+               *err = "string expected"; return NULL;
+       }
+       CONSUME_ONE;
+       return tok;
+}
+
+// The following functions create the branches of the AST.
+
+/*
+RET parse_non_null_type_2(PARAMS) {
+       INIT(non_null_type);
+       parse_list_type(ARGS); REQ;
+       parse_punct(ARGS, '!'); REQ;
+       EXIT;
+}
+
+RET parse_non_null_type_1(PARAMS) {
+       INIT(non_null_type);
+       parse_named_type(ARGS); REQ;
+       parse_punct(ARGS, '!'); REQ;
+       EXIT;
+}
+
+RET parse_non_null_type(PARAMS) {
+       INIT(non_null_type);
+       parse_non_null_type_1(ARGS); OR
+       parse_non_null_type_2(ARGS);
+       EXIT;
+}
+
+RET parse_list_type(PARAMS) {
+       INIT(list_type);
+       parse_punct(ARGS, '['); REQ
+       parse_type(ARGS); REQ
+       parse_punct(ARGS, ']'); REQ
+       EXIT;
+}
+*/
+
+RET parse_named_type(PARAMS) {
+       INIT(named_type);
+       obj->name = parse_name(ARGS);
+       EXIT;
+}
+
+RET parse_type(PARAMS) {
+       INIT(type);
+       obj->named = parse_named_type(ARGS);
+/*
+       OR
+       obj->list = parse_list_type(ARGS); OR
+       obj->non_null = parse_non_null_type(ARGS);
+*/
+       EXIT;
+}
+
+RET parse_variable(PARAMS) {
+       INIT(variable);
+       parse_punct(ARGS, '$'); REQ
+       obj->name = parse_name(ARGS); REQ
+       EXIT;
+}
+
+RET parse_value(PARAMS);
+
+RET parse_list_value(PARAMS) {
+       INIT(list_value);
+       parse_punct(ARGS, '['); REQ
+       parse_punct(ARGS, ']');
+       while (*err) {
+               *err = NULL;
+               parse_value(ARGS); MSG("expected: value or ']'"); REQ
+               parse_punct(ARGS, ']');
+       }
+       EXIT;
+}
+
+RET parse_enum_value(PARAMS) {
+       INIT(enum_value);
+       obj->val = parse_name(ARGS); REQ
+       struct graphql_token *tok = list_top(used, struct graphql_token, node);
+       if (streq(tok->token_string, "true")
+        || streq(tok->token_string, "false")
+        || streq(tok->token_string, "null")) {
+               *err = "enum value cannot be true, false, or null";
+               ROLLBACK(ARGS);
+       }
+       EXIT;
+}
+
+RET parse_null_value(PARAMS) {
+       INIT(null_value);
+       obj->val = parse_keyword(ARGS, "null", "null expected");
+       EXIT;
+}
+
+RET parse_string_value(PARAMS) {
+       INIT(string_value);
+       obj->val = parse_string(ARGS);
+       EXIT;
+}
+
+RET parse_boolean_value(PARAMS) {
+       INIT(boolean_value);
+       obj->val = parse_keyword(ARGS, "true", "invalid boolean value"); OR
+       obj->val = parse_keyword(ARGS, "false", "invalid boolean value");
+       EXIT;
+}
+
+RET parse_float_value(PARAMS) {
+       INIT(float_value);
+       obj->val = parse_float(ARGS);
+       EXIT;
+}
+
+RET parse_int_value(PARAMS) {
+       INIT(int_value);
+       obj->val = parse_int(ARGS);
+       EXIT;
+}
+
+RET parse_object_field(PARAMS) {
+       INIT(object_field);
+       obj->name = parse_name(ARGS); REQ
+       parse_punct(ARGS, ':'); REQ
+       obj->val = parse_value(ARGS); REQ
+       EXIT;
+}
+
+RET parse_object_value(PARAMS) {
+       INIT(object_value);
+       parse_punct(ARGS, '{'); REQ
+       parse_punct(ARGS, '}');
+       struct graphql_object_field *p = NULL;
+       while (*err) {
+               *err = NULL;
+               if (!p) {
+                       obj->first = p = parse_object_field(ARGS); MSG("expected: object field or '}'"); REQ
+               } else {
+                       p->next = parse_object_field(ARGS); MSG("expected: object field or '}'"); REQ
+                       p = p->next;
+               }
+               parse_punct(ARGS, '}');
+       }
+       EXIT;
+}
+
+RET parse_default_value(PARAMS) {
+       INIT(default_value);
+       parse_punct(ARGS, '='); REQ
+       obj->val = parse_value(ARGS); REQ
+       EXIT;
+}
+
+RET parse_value(PARAMS) {
+       INIT(value);
+       obj->var = parse_variable(ARGS); // FIXME: if not const
+       OR
+       obj->int_val = parse_int_value(ARGS); OR
+       obj->float_val = parse_float_value(ARGS); OR
+       obj->str_val = parse_string_value(ARGS); OR
+       obj->bool_val = parse_boolean_value(ARGS); OR
+       obj->null_val = parse_null_value(ARGS); OR
+       obj->enum_val = parse_enum_value(ARGS); OR
+       obj->list_val = parse_list_value(ARGS); OR
+       obj->obj_val = parse_object_value(ARGS);
+       EXIT;
+}
+
+RET parse_type_condition(PARAMS) {
+       INIT(type_condition);
+       parse_keyword(ARGS, "on", "expected: 'on'"); REQ
+       obj->named_type = parse_named_type(ARGS); REQ
+       EXIT;
+}
+
+RET parse_fragment_name(PARAMS) {
+       INIT(fragment_name);
+       obj->name = parse_name(ARGS); REQ
+       struct graphql_token *tok = list_top(used, struct graphql_token, node);
+       if (streq(tok->token_string, "on")) {
+               *err = "invalid fragment name";
+               ROLLBACK(ARGS);
+       }
+       EXIT;
+}
+
+RET parse_alias(PARAMS) {
+       INIT(alias);
+       obj->name = parse_name(ARGS); REQ
+       parse_punct(ARGS, ':'); REQ
+       EXIT;
+}
+
+RET parse_argument(PARAMS) {
+       INIT(argument);
+       obj->name = parse_name(ARGS); REQ
+       parse_punct(ARGS, ':'); REQ
+       obj->val = parse_value(ARGS); REQ
+       EXIT;
+}
+
+RET parse_arguments(PARAMS) {
+       INIT(arguments);
+       parse_punct(ARGS, '('); REQ
+       obj->first = parse_argument(ARGS); REQ
+       struct graphql_argument *p = obj->first;
+       parse_punct(ARGS, ')');
+       while (*err) {
+               *err = NULL;
+               p->next = parse_argument(ARGS); MSG("expected: argument or ')'"); REQ;
+               p = p->next;
+               parse_punct(ARGS, ')');
+       }
+       EXIT;
+}
+
+RET parse_directive(PARAMS) {
+       INIT(directive);
+       parse_punct(ARGS, '@'); REQ
+       obj->name = parse_name(ARGS); REQ
+       obj->args = parse_arguments(ARGS); OPT
+       EXIT;
+}
+
+RET parse_directives(PARAMS) {
+       INIT(directives);
+       obj->first = parse_directive(ARGS); REQ
+       struct graphql_directive *p = obj->first;
+       do {
+               p->next = parse_directive(ARGS);
+               p = p->next;
+       } WHILE_OPT;
+       EXIT;
+}
+
+RET parse_fragment_spread(PARAMS) {
+       INIT(fragment_spread);
+       parse_punct(ARGS, PUNCT_SPREAD); REQ
+       obj->name = parse_fragment_name(ARGS); REQ
+       obj->directives = parse_directives(ARGS); OPT
+       EXIT;
+}
+
+RET parse_variable_definition(PARAMS) {
+       INIT(variable_definition);
+       obj->var = parse_variable(ARGS); REQ
+       parse_punct(ARGS, ':'); REQ
+       obj->type = parse_type(ARGS); REQ
+       obj->default_val = parse_default_value(ARGS); OPT
+       obj->directives = parse_directives(ARGS); OPT
+       EXIT;
+}
+
+RET parse_variable_definitions(PARAMS) {
+       INIT(variable_definitions);
+       parse_punct(ARGS, '('); REQ
+       obj->first = parse_variable_definition(ARGS); REQ
+       struct graphql_variable_definition *p = obj->first;
+       parse_punct(ARGS, ')');
+       while (*err) {
+               *err = NULL;
+               p->next = parse_variable_definition(ARGS); MSG("expected: variable definition or ')'"); REQ
+               p = p->next;
+               parse_punct(ARGS, ')');
+       }
+       EXIT;
+}
+
+RET parse_selection_set(PARAMS);
+
+RET parse_fragment_definition(PARAMS) {
+       INIT(fragment_definition);
+       parse_keyword(ARGS, "fragment", "fragment expected"); REQ
+       obj->name = parse_fragment_name(ARGS); REQ
+       obj->type_cond = parse_type_condition(ARGS); REQ
+       obj->directives = parse_directives(ARGS); OPT
+       obj->sel_set = parse_selection_set(ARGS); REQ
+       EXIT;
+}
+
+RET parse_inline_fragment(PARAMS) {
+       INIT(inline_fragment);
+       parse_punct(ARGS, PUNCT_SPREAD); REQ
+       obj->type_cond = parse_type_condition(ARGS); OPT
+       obj->directives = parse_directives(ARGS); OPT
+       obj->sel_set = parse_selection_set(ARGS); REQ
+       EXIT;
+}
+
+RET parse_field(PARAMS) {
+       INIT(field);
+       obj->alias = parse_alias(ARGS); OPT
+       obj->name = parse_name(ARGS); REQ
+       obj->args = parse_arguments(ARGS); OPT
+       obj->directives = parse_directives(ARGS); OPT
+       obj->sel_set = parse_selection_set(ARGS); OPT
+       EXIT;
+}
+
+RET parse_selection(PARAMS) {
+       INIT(selection);
+       obj->field = parse_field(ARGS); OR
+       obj->frag_spread = parse_fragment_spread(ARGS); OR
+       obj->inline_frag = parse_inline_fragment(ARGS);
+       MSG("expected: field, fragment spread, or inline fragment");
+       EXIT;
+}
+
+RET parse_selection_set(PARAMS) {
+       INIT(selection_set);
+       parse_punct(ARGS, '{'); REQ;
+       obj->first = parse_selection(ARGS); REQ;
+       struct graphql_selection *p = obj->first;
+       parse_punct(ARGS, '}');
+       while (*err) {
+               *err = NULL;
+               p->next = parse_selection(ARGS); MSG("expected: selection or '}'"); REQ;
+               p = p->next;
+               parse_punct(ARGS, '}');
+       }
+       EXIT;
+}
+
+RET parse_operation_type(PARAMS) {
+       INIT(operation_type);
+       const char *errmsg = "expected: query, mutation, or subscription";
+       obj->op_type = parse_keyword(ARGS, "query", errmsg); OR
+       obj->op_type = parse_keyword(ARGS, "mutation", errmsg); OR
+       obj->op_type = parse_keyword(ARGS, "subscription", errmsg);
+       EXIT;
+}
+
+RET parse_operation_definition(PARAMS) {
+       INIT(operation_definition);
+       obj->op_type = parse_operation_type(ARGS);
+       if (!*err) {
+               obj->op_name = parse_name(ARGS); OPT
+               obj->vars = parse_variable_definitions(ARGS); OPT
+               obj->directives = parse_directives(ARGS); OPT
+       } else
+               *err = NULL;
+       obj->sel_set = parse_selection_set(ARGS);
+       if (*err) ROLLBACK(ARGS);
+       EXIT;
+}
+
+RET parse_executable_definition(PARAMS) {
+       INIT(executable_definition);
+       obj->op_def = parse_operation_definition(ARGS); MSG("invalid operation or fragment definition"); OR
+       obj->frag_def = parse_fragment_definition(ARGS); MSG("invalid operation or fragment definition"); 
+       EXIT;
+}
+
+RET parse_executable_document(PARAMS) {
+       INIT(executable_document);
+       obj->first_def = parse_executable_definition(ARGS); REQ
+       struct graphql_executable_definition *p = obj->first_def;
+       do {
+               p->next_def = parse_executable_definition(ARGS);
+               p = p->next_def;
+       } WHILE_OPT;
+       EXIT;
+}
+
+RET parse_definition(PARAMS) {
+       INIT(definition);
+       obj->executable_def = parse_executable_definition(ARGS);
+/*     OR
+       obj->type_system_def = parse_type_system_definition_or_extension(ARGS);
+       // NOTE: Optional type system is not (yet) implemented.
+*/
+       EXIT;
+}
+
+RET parse_document(PARAMS) {
+       INIT(document);
+       obj->first_def = parse_definition(ARGS); REQ
+       struct graphql_definition *p = obj->first_def;
+       do {
+               p->next_def = parse_definition(ARGS);
+               p = p->next_def;
+       } WHILE_OPT;
+       EXIT;
+}
+void *currently_unused = parse_document; // to hide the warning till this is used
+
+/* Convert input string into tokens.
+ *
+ * All data (i.e. the list and the tokens it contains) are allocated to the
+ * specified tal context.
+ */
+const char *graphql_lex(const tal_t *ctx, const char *input, struct list_head **tokens) {
+
+       unsigned int c;
+       const char *p, *line_beginning;
+       unsigned int line_num = 1;
+       struct list_head *tok_list;
+       struct graphql_token *tok;
+
+       // Initialize token output list.
+       tok_list = tal(ctx, struct list_head);
+       if (tokens)
+               *tokens = tok_list;
+       list_head_init(tok_list);
+
+       // Note: label and goto are used here like a continue statement except that
+        // it skips iteration, for when characters are fetched in the loop body.
+       p = input;
+       line_beginning = p;
+       do {
+               c = *p++;
+newchar:
+               // Consume line terminators and increment line counter.
+               if (LINE_TERMINATOR(c)) {
+                       unsigned int c0 = c;
+                       c = *p++;
+                       if (c0 == 10 || c0 == 13)
+                               line_num++;
+                       if (c0 == 13 && c == 10)
+                               c = *p++;
+                       line_beginning = p - 1;
+                       goto newchar;
+               }
+
+               // Consume other ignored tokens.
+               if (COMMA(c) || WHITE_SPACE(c)) {
+                       c = *p++;
+                       goto newchar;
+               }
+               if (COMMENT(c)) {
+                       while (!EOF_CHAR(c) && COMMENT_CHAR(c))
+                               c = *p++;
+                       goto newchar;
+               }
+
+               // Return success when end is reached.
+               if (EOF_CHAR(c))
+                       return GRAPHQL_SUCCESS;
+
+               // Punctuator tokens.
+               if (PUNCTUATOR(c)) {
+
+                       // Note beginning of token in input.
+                       const char *start = p - 1;
+
+                       // Handle the ... multi-character case.
+                       if (c == '.') {
+                               c = *p++;
+                               if (c != '.')
+                                       return "unrecognized punctuator";
+                               c = *p++;
+                               if (c != '.')
+                                       return "unrecognized punctuator";
+                               c = PUNCT_SPREAD;
+                       }
+
+                       tok = talz(tok_list, struct graphql_token);
+                       list_add_tail(tok_list, &tok->node);
+                       tok->token_type = c;
+                       tok->token_string = NULL;
+                       tok->source_line = line_num;
+                       tok->source_column = start - line_beginning + 1;
+                       tok->source_offset = start - input;
+                       tok->source_len = p - start;
+
+               } else if (NAME_START(c)) {
+
+                       // Name/identifier tokens.
+                       tok = talz(tok_list, struct graphql_token);
+                       list_add_tail(tok_list, &tok->node);
+                       tok->token_type = 'a';
+                       // tok->token_string updated below.
+                       tok->source_line = line_num;
+                       tok->source_column = p - line_beginning;
+                       // tok->source_len updated below.
+
+                       // Note the beginning of the name.
+                       const char *name_begin = p - 1;
+                       const char *name_end;
+                       int name_len;
+
+                       // Consume the rest of the token.
+                       do {
+                               c = *p++;
+                       } while (NAME_CONTINUE(c));
+
+                       // Note the end of the name and calculate the length.
+                       name_end = p - 1;
+                       name_len = name_end - name_begin;
+                       tok->source_offset = name_begin - input;
+                       tok->source_len = name_len;
+
+                       // Copy the token string.
+                       tok->token_string = tal_strndup(tok, name_begin, name_len);
+
+                       goto newchar;
+
+               } else if (DIGIT(c) || c == '-') {
+
+                       // Number tokens.
+                       const char *num_start = p - 1;
+                       char type = 'i';
+
+                       if (c == '-') {
+                               c = *p++;
+                               if (!DIGIT(c))
+                                       return "negative sign must precede a number";
+                       }
+
+                       if (c == '0') {
+                               c = *p++;
+                               if (DIGIT(c))
+                                       return "leading zeros are not allowed";
+                       } else {
+                               do {
+                                       c = *p++;
+                               } while(DIGIT(c));
+                       }
+
+                       if (c == '.') {
+                               type = 'f';
+                               if (!DIGIT(*p))
+                                       return "invalid float value fractional part";
+                               do {
+                                       c = *p++;
+                               } while(DIGIT(c));
+                       }
+
+                       if (c == 'e' || c == 'E') {
+                               type = 'f';
+                               c = *p++;
+                               if (c == '+' || c == '-')
+                                       c = *p++;
+                               if (!DIGIT(*p))
+                                       return "invalid float value exponent part";
+                               do {
+                                       c = *p++;
+                               } while(DIGIT(c));
+                       }
+
+                       if (c == '.' || NAME_START(c))
+                               return "invalid numeric value";
+
+                       const char *num_end = p - 1;
+                       int num_len = num_end - num_start;
+
+                       tok = talz(tok_list, struct graphql_token);
+                       list_add_tail(tok_list, &tok->node);
+                       tok->token_type = type;
+                       tok->token_string = tal_strndup(tok, num_start, num_len);
+                       tok->source_line = line_num;
+                       tok->source_column = num_start - line_beginning + 1;
+                       tok->source_offset = num_start - input;
+                       tok->source_len = num_len;
+
+                       goto newchar;
+
+               } else if (c == '"') {
+
+                       // String tokens.
+                       c = *p++;
+                       const char *str_begin = p - 1;
+                       const char *str_end;
+                       bool str_block = false;
+                       if (c == '"') {
+                               c = *p++;
+                               if (c == '"') {
+                                       // block string
+                                       str_block = true;
+                                       str_begin += 2;
+                                       int quotes = 0;
+                                       do {
+                                               c = *p++;
+                                               if (c == '\"') quotes++; else quotes = 0;
+                                               if (quotes == 3 && *(p-4) == '\\') quotes = 0;
+                                       } while (BLOCK_STRING_CHAR(c) && quotes < 3);
+                                       if (quotes == 3) {
+                                               c = *--p;
+                                               c = *--p;
+                                       }
+                                       str_end = p - 1;
+                                       if (c != '"')
+                                               return "unterminated string or invalid character";
+                                       c = *p++;
+                                       if (c != '"')
+                                               return "invalid string termination";
+                                       c = *p++;
+                                       if (c != '"')
+                                               return "invalid string termination";
+                               } else {
+                                       // empty string
+                                       str_end = str_begin;
+                                       --p;
+                               }
+                       } else {
+                               // normal string
+                               --p;
+                               do {
+                                       c = *p++;
+                                       if (c == '\\') {
+                                               c = *p++;
+                                               if (strchr("\"\\/bfnrtu", c)) {
+                                                       if (c == 'u') {
+                                                               c = *p++;
+                                                               if (!HEX_DIGIT(c))
+                                                                       return "invalid unicode escape sequence";
+                                                               c = *p++;
+                                                               if (!HEX_DIGIT(c))
+                                                                       return "invalid unicode escape sequence";
+                                                               c = *p++;
+                                                               if (!HEX_DIGIT(c))
+                                                                       return "invalid unicode escape sequence";
+                                                               c = *p++;
+                                                               if (!HEX_DIGIT(c))
+                                                                       return "invalid unicode escape sequence";
+                                                       } else {
+                                                               c = 'a'; // anything besides a quote to let the loop continue
+                                                       }
+                                               } else {
+                                                       return "invalid string escape sequence";
+                                               }
+                                       }
+                               } while (STRING_CHAR(c));
+                               if (c != '"')
+                                       return "unterminated string or invalid character";
+                               str_end = p - 1;
+                       }
+                       int str_len = str_end - str_begin;
+
+                       tok = talz(tok_list, struct graphql_token);
+                       list_add_tail(tok_list, &tok->node);
+                       tok->token_type = 's';
+                       tok->token_string = tal_strndup(tok, str_begin, str_len);
+                       tok->source_line = line_num;
+                       tok->source_column = str_begin - line_beginning + 1;
+                       tok->source_offset = str_begin - input;
+                       tok->source_len = str_len;
+
+                       // Process escape sequences. These always shorten the string (so the memory allocation is always enough).
+                       char d;
+                       char *q = tok->token_string;
+                       char *rewrite_dest;
+                       int quotes = 0;
+                       while ((d = *q++)) {
+                               if (str_block) {
+                                       if (d == '\"') quotes++; else quotes = 0;
+                                       if (quotes == 3 && *(q-4) == '\\') {
+                                               quotes = 0;
+                                               rewrite_dest = q - 4;
+                                               cpystr(rewrite_dest, q - 3);
+                                       }
+                               } else {
+                                       if (d == '\\') {
+                                               rewrite_dest = q - 1;
+                                               d = *q++;
+                                               switch (d) {
+                                               case '\"':
+                                                       *rewrite_dest++ = '\"';
+                                                       cpystr(rewrite_dest, q--);
+                                                       break;
+                                               case 'b':
+                                                       *rewrite_dest++ = '\b';
+                                                       cpystr(rewrite_dest, q--);
+                                                       break;
+                                               case 'f':
+                                                       *rewrite_dest++ = '\f';
+                                                       cpystr(rewrite_dest, q--);
+                                                       break;
+                                               case 'n':
+                                                       *rewrite_dest++ = '\n';
+                                                       cpystr(rewrite_dest, q--);
+                                                       break;
+                                               case 'r':
+                                                       *rewrite_dest++ = '\r';
+                                                       cpystr(rewrite_dest, q--);
+                                                       break;
+                                               case 't':
+                                                       *rewrite_dest++ = '\t';
+                                                       cpystr(rewrite_dest, q--);
+                                                       break;
+                                               case 'u': {
+                                                               // Insert escaped character using UTF-8 multi-byte encoding.
+                                                               char buf[5], *b = buf;
+                                                               for (int i = 0; i < 4; i++)
+                                                                       *b++ = *q++;
+                                                               *b = 0;
+                                                               int code_point = strtol(buf, 0, 16);
+                                                               int bytes = utf8_encode(code_point, rewrite_dest);
+                                                               // note: if bytes == 0
+                                                               // due to encoding failure,
+                                                               // the following will safely
+                                                               // eliminate the invalid char.
+                                                               rewrite_dest += bytes;
+                                                               cpystr(rewrite_dest, q--);
+                                                       }
+                                                       break;
+                                               default:
+                                                       cpystr(rewrite_dest, --q);
+                                               }
+                                       }
+                               }
+                       }
+                       if (str_block) {
+                               // Strip leading lines.
+                               q = tok->token_string;
+                               for (;;) {
+                                       d = *q++;
+                                       while (WHITE_SPACE(d))
+                                               d = *q++;
+                                       if (LINE_TERMINATOR(d)) {
+                                               while (LINE_TERMINATOR(d))
+                                                       d = *q++;
+                                               cpystr(tok->token_string, q - 1);
+                                               q = tok->token_string;
+                                       } else
+                                               break;
+                               }
+
+                               // Strip trailing lines.
+                               q = tok->token_string + strlen(tok->token_string);
+                               for (;;) {
+                                       d = *--q;
+                                       while (WHITE_SPACE(d))
+                                               d = *--q;
+                                       if (LINE_TERMINATOR(d)) {
+                                               while (LINE_TERMINATOR(d))
+                                                       d = *--q;
+                                               *++q = 0;
+                                       } else
+                                               break;
+                               }
+
+                               // Look for common indentation.
+                               char *this_indent_start;
+                               const char *this_indent_end;
+                               const char *common_indent_start = NULL;
+                               const char *common_indent_end = common_indent_start;
+                               const char *r;
+                               q = tok->token_string;
+                               do {
+                                       d = *q++;
+                                       this_indent_start = q - 1;
+                                       while (WHITE_SPACE(d))
+                                               d = *q++;
+                                       this_indent_end = q - 1;
+                                       if (LINE_TERMINATOR(d)) {
+                                               while (LINE_TERMINATOR(d))
+                                                       d = *q++;
+                                               continue;
+                                       }
+                                       if (EOF_CHAR(d))
+                                               continue;
+
+                                       if (common_indent_start == NULL) {
+                                               common_indent_start = this_indent_start;
+                                               common_indent_end = this_indent_end;
+                                       }
+                                       for (r = this_indent_start; r < this_indent_end && (r - this_indent_start + common_indent_start < common_indent_end); r++) {
+                                               if (*r != *(r - this_indent_start + common_indent_start))
+                                                       break;
+                                       }
+                                       common_indent_end = r - this_indent_start + common_indent_start;
+
+                                       while (!LINE_TERMINATOR(d) && !EOF_CHAR(d))
+                                               d = *q++;
+                                       while (LINE_TERMINATOR(d))
+                                               d = *q++;
+                                       --q;
+
+                               } while (d);
+
+                               // Remove common indentation.
+                               int common_indent_len = common_indent_end - common_indent_start;
+                               if (common_indent_len > 0) {
+                                       q = tok->token_string;
+                                       do {
+                                               d = *q++;
+                                               this_indent_start = q - 1;
+                                               while (WHITE_SPACE(d))
+                                                       d = *q++;
+                                               this_indent_end = q - 1;
+                                               if (LINE_TERMINATOR(d)) {
+                                                       while (LINE_TERMINATOR(d))
+                                                               d = *q++;
+                                                       continue;
+                                               }
+                                               if (EOF_CHAR(d))
+                                                       continue;
+
+                                               while (!LINE_TERMINATOR(d) && !EOF_CHAR(d))
+                                                       d = *q++;
+                                               --q;
+
+                                               cpystr(this_indent_start, this_indent_start + common_indent_len);
+                                               q -= common_indent_len;
+                                               d = *q++;
+
+                                               while (LINE_TERMINATOR(d))
+                                                       d = *q++;
+                                               --q;
+
+                                       } while (d);
+                               }
+                       }
+                       c = *p++;
+                       goto newchar;
+
+               } else {
+                       return "invalid source character encountered";
+               }
+
+       } while (!EOF_CHAR(c));
+
+       return "unexpected end-of-input encountered";
+}
+
+// Convert lexed tokens into AST.
+const char *graphql_parse(struct list_head *tokens, struct graphql_executable_document **doc) {
+       struct list_head used = LIST_HEAD_INIT(used);
+       const char *err = NULL;
+       *doc = parse_executable_document(tokens, &used, &err);
+       return err;
+}
+
+// Convert input string into AST.
+const char *graphql_lexparse(const tal_t *ctx, const char *input, struct list_head **tokens, struct graphql_executable_document **doc) {
+       const char *err = graphql_lex(ctx, input, tokens);
+       if (!err)
+               err = graphql_parse(*tokens, doc);
+       return err;
+}
+
+
+
diff --git a/ccan/graphql/graphql.h b/ccan/graphql/graphql.h
new file mode 100644 (file)
index 0000000..30116d4
--- /dev/null
@@ -0,0 +1,291 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef __GRAPHQL_H__
+#define __GRAPHQL_H__ 1
+
+#include <stdio.h>
+
+#include <ccan/list/list.h>
+#include <ccan/tal/tal.h>
+
+// Coding constants
+#define GRAPHQL_SUCCESS ((const char *)NULL)
+
+// The following structures constitute the AST returned by the parser.
+
+struct graphql_directive {
+       struct graphql_directive *next;
+       struct graphql_token *name;
+       struct graphql_arguments *args;
+       void *data; // for application use
+};
+
+struct graphql_directives {
+       struct graphql_directive *first;
+       void *data; // for application use
+};
+
+struct graphql_named_type {
+       struct graphql_token *name;
+       void *data; // for application use
+};
+
+struct graphql_type {
+       struct graphql_named_type *named;
+//     struct graphql_list_type *list;
+//     struct graphql_non_null_type *non_null;
+       void *data; // for application use
+};
+
+struct graphql_default_value {
+       struct graphql_value *val;
+       void *data; // for application use
+};
+
+struct graphql_variable_definition {
+       struct graphql_variable_definition *next;
+       struct graphql_variable *var;
+       struct graphql_type *type;
+       struct graphql_default_value *default_val;
+       struct graphql_directives *directives;
+       void *data; // for application use
+};
+
+struct graphql_variable_definitions {
+       struct graphql_variable_definition *first;
+       void *data; // for application use
+};
+
+struct graphql_variable {
+       struct graphql_token *name;
+       void *data; // for application use
+};
+
+struct graphql_object_field {
+       struct graphql_object_field *next;
+       struct graphql_token *name;
+       struct graphql_value *val;
+       void *data; // for application use
+};
+
+struct graphql_object_value {
+       struct graphql_object_field *first;
+       void *data; // for application use
+};
+
+struct graphql_list_value {
+       struct graphql_token *val;
+       void *data; // for application use
+};
+
+struct graphql_enum_value {
+       struct graphql_token *val;
+       void *data; // for application use
+};
+
+struct graphql_null_value {
+       struct graphql_token *val;
+       void *data; // for application use
+};
+
+struct graphql_string_value {
+       struct graphql_token *val;
+       void *data; // for application use
+};
+
+struct graphql_boolean_value {
+       struct graphql_token *val;
+       void *data; // for application use
+};
+
+struct graphql_float_value {
+       struct graphql_token *val;
+       void *data; // for application use
+};
+
+struct graphql_int_value {
+       struct graphql_token *val;
+       void *data; // for application use
+};
+
+struct graphql_value {
+       struct graphql_variable *var;
+       struct graphql_int_value *int_val;
+       struct graphql_float_value *float_val;
+       struct graphql_boolean_value *bool_val;
+       struct graphql_string_value *str_val;
+       struct graphql_null_value *null_val;
+       struct graphql_enum_value *enum_val;
+       struct graphql_list_value *list_val;
+       struct graphql_object_value *obj_val;
+       void *data; // for application use
+};
+
+struct graphql_inline_fragment {
+       struct graphql_type_condition *type_cond;
+       struct graphql_directives *directives;
+       struct graphql_selection_set *sel_set;
+       void *data; // for application use
+};
+
+struct graphql_type_condition {
+       struct graphql_named_type *named_type;
+       void *data; // for application use
+};
+
+struct graphql_fragment_name {
+       struct graphql_token *name;
+       void *data; // for application use
+};
+
+struct graphql_fragment_definition {
+       struct graphql_fragment_name *name;
+       struct graphql_type_condition *type_cond;
+       struct graphql_directives *directives;
+       struct graphql_selection_set *sel_set;
+       void *data; // for application use
+};
+
+struct graphql_fragment_spread {
+       struct graphql_fragment_name *name;
+       struct graphql_directives *directives;
+       void *data; // for application use
+};
+
+struct graphql_alias {
+       struct graphql_token *name;
+       void *data; // for application use
+};
+
+struct graphql_argument {
+       struct graphql_argument *next;
+       struct graphql_token *name;
+       struct graphql_value *val;
+       void *data; // for application use
+};
+
+struct graphql_arguments {
+       struct graphql_argument *first;
+       void *data; // for application use
+};
+
+struct graphql_field {
+       struct graphql_alias *alias;
+       struct graphql_token *name;
+       struct graphql_arguments *args;
+       struct graphql_directives *directives;
+       struct graphql_selection_set *sel_set;
+       void *data; // for application use
+};
+
+struct graphql_selection {
+       struct graphql_selection *next;
+       struct graphql_field *field;
+       struct graphql_fragment_spread *frag_spread;
+       struct graphql_inline_fragment *inline_frag;
+       void *data; // for application use
+};
+
+struct graphql_selection_set {
+       struct graphql_selection *first;
+       void *data; // for application use
+};
+
+struct graphql_operation_type {
+       struct graphql_token *op_type;
+       void *data; // for application use
+};
+
+struct graphql_operation_definition {
+       struct graphql_operation_type *op_type;
+       struct graphql_token *op_name;
+       struct graphql_variable_definitions *vars;
+       struct graphql_directives *directives;
+       struct graphql_selection_set *sel_set;
+       void *data; // for application use
+};
+
+struct graphql_executable_definition {
+       struct graphql_executable_definition *next_def;
+       struct graphql_operation_definition *op_def;
+       struct graphql_fragment_definition *frag_def;
+       void *data; // for application use
+};
+
+struct graphql_executable_document {
+       struct graphql_executable_definition *first_def;
+       void *data; // for application use
+};
+
+struct graphql_definition {
+       struct graphql_definition *next_def;
+       struct graphql_executable_definition *executable_def;
+       struct graphql_type_system_definition_or_extension *type_system_def;
+       void *data; // for application use
+};
+
+struct graphql_document {
+       struct graphql_definition *first_def;
+       void *data; // for application use
+};
+
+enum token_type_enum {
+       NAME            = 'a',
+       INTEGER         = 'i',
+       FLOAT           = 'f',
+       STRING          = 's',
+       PUNCT_BANG      = '!',
+       PUNCT_SH__      = '$',
+       PUNCT_AMP       = '&',
+       PUNCT_LPAR      = '(',
+       PUNCT_RPAR      = ')',
+       PUNCT_COLON     = ':',
+       PUNCT_EQ        = '=',
+       PUNCT_AT        = '@',
+       PUNCT_LBRACKET  = '[',
+       PUNCT_RBRACKET  = ']',
+       PUNCT_LBRACE    = '{',
+       PUNCT_PIPE      = '|',
+       PUNCT_RBRACE    = '}',
+       PUNCT_SPREAD    = 0x2026, // spread operator (triple dot)
+};
+
+struct graphql_token {
+       struct list_node node;
+       enum token_type_enum token_type;
+       char *token_string;
+       unsigned int source_line;
+       unsigned int source_column;
+       unsigned int source_offset;
+       unsigned int source_len;
+       void *data; // for application use
+};
+
+/* The lexer.
+ * INPUTS:
+ *     input - string to parse
+ *     ctx - parent tal context or NULL
+ *     tokens - a variable to receive the resulting token list
+ * RETURN:
+ *     GRAPHQL_SUCCESS or an error string.
+ */
+const char *graphql_lex(const tal_t *ctx, const char *input, struct list_head **tokens);
+
+/* The parser.
+ * INPUTS:
+ *     tokens - the list produced by the lexer
+ *     doc - a variable to receive the resulting abstract syntax tree (AST)
+ * OPERATION:
+ *     The token list is emptied during parsing, so far as the parsing
+ *     succeeds. This allows the caller to inspect the line/char position
+ *     of the next token (where the error likely is) and report that hint to
+ *     the user in the form of an error message.
+ * RETURN:
+ *     GRAPHQL_SUCCESS or an error string.
+ */
+const char *graphql_parse(struct list_head *tokens, struct graphql_executable_document **doc);
+
+/* The lexer and parser in one function, for convenience. */
+const char *graphql_lexparse(const tal_t *ctx, const char *input, struct list_head **tokens, struct graphql_executable_document **doc);
+
+#endif
+
diff --git a/ccan/graphql/test/run.c b/ccan/graphql/test/run.c
new file mode 100644 (file)
index 0000000..7ae1678
--- /dev/null
@@ -0,0 +1,3177 @@
+/* Include the C files directly. */
+#include "ccan/graphql/graphql.c"
+#include "ccan/str/str.h"
+
+/* TEST POINT MACROS
+ *
+ * The idea here is to run each test case silently, and if any test point
+ * fails, that test case is re-run verbosely to pinpoint the failure.
+ *
+ * RUN macros run a whole test case function.
+ *
+ * Different TEST_xxxx macros are provided for different test point types as
+ * follows:
+ *
+ *  TEST_CONT - Test and continue testing regardless of failure (the failure
+ *             will still be printed).
+ *  TEST_ABRT - Test and abort the test case, used when testing pointer
+ *             validity to avoid subsequent dereference errors.
+ *  TEST_STRG - Test a string value, which includes the convenience of testing
+ *             for a null pointer.
+ */
+
+#define RUN(test) { prev_fail = fail; test(source); if (fail != prev_fail) { mute = false; test(source); mute = true; } }
+#define RUN1(test,arg) { prev_fail = fail; test(source,arg); if (fail != prev_fail) { mute = false; test(source,arg); mute = true; } }
+#define RUN2(test,arg1,arg2) { prev_fail = fail; test(source,arg1,arg2); if (fail != prev_fail) { mute = false; test(source,arg1,arg2); mute = true; } }
+
+#define TEST_CONT(expr) { bool c = (expr); if (mute) c? pass++ : fail++; else printf("%s: %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(expr)); }
+#define TEST_ABRT(expr) { bool c = (expr); if (mute) c? pass++ : fail++; else printf("%s: %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(expr)); if (!c) return; }
+#define TEST_STRG(str,expr) { bool c = ((str) && streq((str),(expr))); if (mute) c? pass++ : fail++; else printf("%s: %s == %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(str), stringify(expr)); if (!c) return; }
+
+// Global variables to track overall results.
+int pass = 0, fail = 0;
+bool mute = 1;
+
+// Helper function.
+int listlen(struct list_head *tokens);
+int listlen(struct list_head *tokens) {
+       struct graphql_token *tok;
+       int n=0;
+       list_for_each(tokens, tok, node) {
+               n++;
+       }
+       return n;
+}
+
+/* Test case function prototypes */
+
+void check_example_3(const char *source);
+void check_example_5(char *source);
+void check_example_6(char *source);
+void check_example_7(char *source);
+void check_example_8(char *source);
+void check_example_9(char *source);
+void check_example_10(char *source);
+void check_example_11(char *source);
+void check_example_12_and_13(const char *source);
+void check_example_14(char *source);
+void check_example_16(char *source);
+void check_example_18(char *source);
+void check_example_19(char *source);
+void check_example_20(char *source);
+void check_example_21(char *source);
+void check_example_23(char *source);
+void check_example_24(char *source);
+void check_int_value(char *source, int int_value);
+void check_invalid_int_values(char *source);
+void check_float_value(char *source, float float_value, const char *format);
+void check_valid_float_values(char *source);
+void check_invalid_float_values(char *source);
+void check_boolean_values(char *source);
+void check_string_value(char *source, const char *test_value, const char *expected_result);
+void check_example_25_and_26(const char *source);
+void check_example_29(char *source);
+void check_example_30_and_31(const char *source);
+void check_example_32(char *source);
+void check_example_34(char *source);
+void check_example_35(char *source);
+
+/* Test case functions begin here, called by main().
+ * Note: Memory should be freed correctly in the success case, but if there
+ * are errors, all bets are off.
+ */
+
+void check_example_3(const char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 3\n");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 11);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '{');
+       TEST_CONT(tok->source_line == 1);
+       TEST_CONT(tok->source_column == 1);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "user");
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 3);
+       TEST_CONT(tok->source_len == 4);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '(');
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 7);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 8);
+       TEST_CONT(tok->source_len == 2);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == ':');
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 10);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "4");
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 12);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == ')');
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 13);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '{');
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 15);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       TEST_CONT(tok->source_line == 3);
+       TEST_CONT(tok->source_column == 5);
+       TEST_CONT(tok->source_len == 4);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '}');
+       TEST_CONT(tok->source_line == 4);
+       TEST_CONT(tok->source_column == 3);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '}');
+       TEST_CONT(tok->source_line == 5);
+       TEST_CONT(tok->source_column == 1);
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name");
+       tokens = tal_free(tokens);
+}
+
+void check_example_5(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 5\n");
+
+       sprintf(source, "\
+mutation {\n\
+  likeStory(storyID: 12345) {\n\
+    story {\n\
+      likeCount\n\
+    }\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 15);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "mutation");
+       TEST_CONT(tok->source_line == 1);
+       TEST_CONT(tok->source_column == 1);
+       TEST_CONT(tok->source_len == 8);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "likeStory");
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 3);
+       TEST_CONT(tok->source_len == 9);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "storyID");
+       TEST_CONT(tok->source_line == 2);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "12345");
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 22);
+       TEST_CONT(tok->source_len == 5);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "story");
+       TEST_CONT(tok->source_line == 3);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "likeCount");
+       TEST_CONT(tok->source_line == 4);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_ABRT(doc->first_def->op_def->op_type != NULL);
+       TEST_ABRT(doc->first_def->op_def->op_type->op_type != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type->op_type->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->op_type->op_type->token_string, "mutation");
+       TEST_ABRT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "likeStory");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "storyID");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "12345");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "story");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name->token_string, "likeCount");
+       tokens = tal_free(tokens);
+}
+
+void check_example_6(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 6\n");
+
+       sprintf(source, "\
+{\n\
+  field\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 3);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "field");
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 3);
+       TEST_CONT(tok->source_len == 5);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_ABRT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "field");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_7(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 7\n");
+
+       sprintf(source, "\
+{\n\
+  id\n\
+  firstName\n\
+  lastName\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 5);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 3);
+       TEST_CONT(tok->source_len == 2);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "firstName");
+       TEST_CONT(tok->source_line == 3);
+       TEST_CONT(tok->source_column == 3);
+       TEST_CONT(tok->source_len == 9);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "lastName");
+       TEST_CONT(tok->source_line == 4);
+       TEST_CONT(tok->source_column == 3);
+       TEST_CONT(tok->source_len == 8);
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node);
+       TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_ABRT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "id");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->next->field->name->token_string, "firstName");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->next->next->field->name->token_string, "lastName");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_8(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 8\n");
+
+       sprintf(source, "\
+{\n\
+  me {\n\
+    id\n\
+    firstName\n\
+    lastName\n\
+    birthday {\n\
+      month\n\
+      day\n\
+    }\n\
+    friends {\n\
+      name\n\
+    }\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 17);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "me");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "firstName");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "lastName");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "birthday");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "month");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "day");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "friends");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_ABRT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "me");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "firstName");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "lastName");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_string, "birthday");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name->token_string, "month");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name->token_string, "day");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->sel_set == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name->token_string, "friends");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name->token_string, "name");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->sel_set == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_9(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 9\n");
+
+       sprintf(source, "\
+# `me` could represent the currently logged in viewer.\n\
+{\n\
+  me {\n\
+    name\n\
+  }\n\
+}\n\
+\n\
+# `user` represents one of many users in a graph of data, referred to by a\n\
+# unique identifier.\n\
+{\n\
+  user(id: 4) {\n\
+    name\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 17);
+       // NOTE: Comments are ignored.
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "me");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "user");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "4");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->next_def != NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_ABRT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "me");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->next_def->next_def == NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->name->token_string, "user");
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name->token_string, "id");
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4");
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name");
+       tokens = tal_free(tokens);
+}
+
+void check_example_10(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 10\n");
+
+       sprintf(source, "\
+{\n\
+  user(id: 4) {\n\
+    id\n\
+    name\n\
+    profilePic(size: 100)\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 18);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "user");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "4");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "profilePic");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "size");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "100");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "100");
+       tokens = tal_free(tokens);
+}
+
+void check_example_11(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 11\n");
+
+       sprintf(source, "\
+{\n\
+  user(id: 4) {\n\
+    id\n\
+    name\n\
+    profilePic(width: 100, height: 50)\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 21);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "user");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "4");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "profilePic");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "width");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "100");
+       // NOTE: Comma is ignored.
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "height");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "50");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "width");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "100");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name->token_string, "height");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val->token_string, "50");
+       tokens = tal_free(tokens);
+}
+
+void check_example_12_and_13(const char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example Nos. 12 and 13\n");
+
+       // Test the lexer.
+       const char *param;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 11);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "picture");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_ABRT(tok->token_string != NULL && (streq(tok->token_string, "width") || streq(tok->token_string, "height")));
+       param = tok->token_string;
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_CONT(tok->token_string != NULL && ((streq(param, "width") && streq(tok->token_string, "200")) || (streq(param, "height") && streq(tok->token_string, "100"))));
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_CONT(tok->token_string != NULL && (streq(tok->token_string, "width") || streq(tok->token_string, "height")));
+       param = tok->token_string;
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_CONT(tok->token_string != NULL && ((streq(param, "width") && streq(tok->token_string, "200")) || (streq(param, "height") && streq(tok->token_string, "100"))));
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_argument *arg;
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "picture");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next->next == NULL);
+       arg = doc->first_def->op_def->sel_set->first->field->args->first;
+       if (!streq(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "width")) arg = arg->next;
+       TEST_ABRT(arg->name != NULL);
+       TEST_CONT(arg->name->token_type == 'a');
+       TEST_STRG(arg->name->token_string, "width");
+       TEST_ABRT(arg->val != NULL);
+       TEST_ABRT(arg->val->int_val != NULL);
+       TEST_ABRT(arg->val->int_val->val != NULL);
+       TEST_CONT(arg->val->int_val->val->token_type == 'i');
+       TEST_STRG(arg->val->int_val->val->token_string, "200");
+       arg = doc->first_def->op_def->sel_set->first->field->args->first;
+       if (!streq(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "height")) arg = arg->next;
+       TEST_ABRT(arg->name != NULL);
+       TEST_CONT(arg->name->token_type == 'a');
+       TEST_STRG(arg->name->token_string, "height");
+       TEST_ABRT(arg->val != NULL);
+       TEST_ABRT(arg->val->int_val != NULL);
+       TEST_ABRT(arg->val->int_val->val != NULL);
+       TEST_CONT(arg->val->int_val->val->token_type == 'i');
+       TEST_STRG(arg->val->int_val->val->token_string, "100");
+       tokens = tal_free(tokens);
+}
+
+void check_example_14(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 14\n");
+
+       sprintf(source, "\
+{\n\
+  user(id: 4) {\n\
+    id\n\
+    name\n\
+    smallPic: profilePic(size: 64)\n\
+    bigPic: profilePic(size: 1024)\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 28);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "user");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "4");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "smallPic");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "profilePic");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "size");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "64");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "bigPic");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "profilePic");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "size");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "1024");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name->token_string, "smallPic");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "64");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name->token_string, "bigPic");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_string, "profilePic");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name->token_string, "size");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val->token_string, "1024");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_16(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 16\n");
+
+       sprintf(source, "\
+{\n\
+  zuck: user(id: 4) {\n\
+    id\n\
+    name\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 14);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "zuck");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "user");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, "4");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->alias != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->alias->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->alias->name->token_string, "zuck");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_18(char *source) {
+       struct list_head *tokens;
+
+       if (!mute) printf("// Example No. 18\n");
+
+       sprintf(source, "\
+query noFragments {\n\
+  user(id: 4) {\n\
+    friends(first: 10) {\n\
+      id\n\
+      name\n\
+      profilePic(size: 50)\n\
+    }\n\
+    mutualFriends(first: 10) {\n\
+      id\n\
+      name\n\
+      profilePic(size: 50)\n\
+    }\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 44);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next->next->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next->next->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_19(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 19\n");
+
+       sprintf(source, "\
+query withFragments {\n\
+  user(id: 4) {\n\
+    friends(first: 10) {\n\
+      ...friendFields\n\
+    }\n\
+    mutualFriends(first: 10) {\n\
+      ...friendFields\n\
+    }\n\
+  }\n\
+}\n\
+\n\
+fragment friendFields on User {\n\
+  id\n\
+  name\n\
+  profilePic(size: 50)\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 46);
+       for (int i=0; i<17; i++)
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "friendFields");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       for (int i=0; i<7; i++)
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "friendFields");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       const char *e;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL);
+       if (e) printf("%s\n", e);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_string, "friendFields");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->field == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_string, "friendFields");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL);
+       TEST_ABRT(doc->first_def->next_def != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def == NULL);
+       TEST_CONT(doc->first_def->next_def->op_def == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "friendFields");
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User");
+       TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_20(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 20\n");
+
+       sprintf(source, "\
+query withNestedFragments {\n\
+  user(id: 4) {\n\
+    friends(first: 10) {\n\
+      ...friendFields\n\
+    }\n\
+    mutualFriends(first: 10) {\n\
+      ...friendFields\n\
+    }\n\
+  }\n\
+}\n\
+\n\
+fragment friendFields on User {\n\
+  id\n\
+  name\n\
+  ...standardProfilePic\n\
+}\n\
+\n\
+fragment standardProfilePic on User {\n\
+  profilePic(size: 50)\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 54);
+       for (int i=0; i<17; i++)
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "friendFields");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       for (int i=0; i<7; i++)
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "friendFields");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       const char *e;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL);
+       if (e) printf("%s\n", e);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_string, "friendFields");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->field == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_string, "friendFields");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL);
+       TEST_ABRT(doc->first_def->next_def != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "friendFields");
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User");
+       TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->next->field == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->frag_spread != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->next == NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->next_def == NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->op_def == NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name->name != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->next_def->frag_def->name->name->token_string, "standardProfilePic");
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_string, "User");
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->directives == NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->sel_set->first->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_21(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 21\n");
+
+       sprintf(source, "\
+query FragmentTyping {\n\
+  profiles(handles: [\"zuck\", \"coca-cola\"]) {\n\
+    handle\n\
+    ...userFragment\n\
+    ...pageFragment\n\
+  }\n\
+}\n\
+\n\
+fragment userFragment on User {\n\
+  friends {\n\
+    count\n\
+  }\n\
+}\n\
+\n\
+fragment pageFragment on Page {\n\
+  likers {\n\
+    count\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 40);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "query");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "FragmentTyping");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "profiles");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "handles");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '[');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       TEST_STRG(tok->token_string, "zuck");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       TEST_STRG(tok->token_string, "coca-cola");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ']');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       const char *e;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL);
+       if (e) printf("%s\n", e);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name->token_string, "userFragment");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name->token_string, "pageFragment");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL);
+       TEST_ABRT(doc->first_def->next_def != NULL);
+       TEST_CONT(doc->first_def->next_def->op_def == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "userFragment");
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User");
+       TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->next_def == NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->op_def == NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name->name != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->name->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->next_def->frag_def->name->name->token_string, "pageFragment");
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a');
+       TEST_STRG(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_string, "Page");
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->directives == NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->next_def->next_def->frag_def->sel_set->first->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_23(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 23\n");
+
+       sprintf(source, "\
+query inlineFragmentTyping {\n\
+  profiles(handles: [\"zuck\", \"coca-cola\"]) {\n\
+    handle\n\
+    ... on User {\n\
+      friends {\n\
+        count\n\
+      }\n\
+    }\n\
+    ... on Page {\n\
+      likers {\n\
+        count\n\
+      }\n\
+    }\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 34);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "query");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "inlineFragmentTyping");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "profiles");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "handles");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '[');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       TEST_STRG(tok->token_string, "zuck");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       TEST_STRG(tok->token_string, "coca-cola");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ']');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       const char *e;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL);
+       if (e) printf("%s\n", e);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name->token_string, "User");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name->token_string, "Page");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_24(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 24\n");
+
+       sprintf(source, "\
+query inlineFragmentNoType($expandedInfo: Boolean) {\n\
+  user(handle: \"zuck\") {\n\
+    id\n\
+    name\n\
+    ... @include(if: $expandedInfo) {\n\
+      firstName\n\
+      lastName\n\
+      birthday\n\
+    }\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 34);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "query");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "inlineFragmentNoType");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "expandedInfo");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "Boolean");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "user");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "handle");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       TEST_STRG(tok->token_string, "zuck");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "id");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "name");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "include");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "if");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "expandedInfo");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "firstName");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "lastName");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "birthday");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name->token_string, "include");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name->token_string, "if");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->int_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->float_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->bool_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->str_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->null_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->enum_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->list_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->obj_val == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name->token_string, "expandedInfo");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next->next->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_int_value(char *source, int int_value) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Int Value Range Check on %d\n", int_value);
+
+       sprintf(source, "\
+{\n\
+  user(id: %d) {\n\
+    name\n\
+  }\n\
+}\n\
+       ", int_value);
+
+       char buf[20];
+       sprintf(buf, "%d", int_value);
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 11);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       TEST_STRG(tok->token_string, buf);
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 12);
+       TEST_CONT(tok->source_len == strlen(buf));
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 12 + strlen(buf));
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, buf);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_invalid_int_values(char *source) {
+       struct list_head *tokens;
+
+       if (!mute) printf("// Invalid Int Values\n");
+
+       const char *bad_values[] = {"00", "-00", "+1", "1.", "1a", "1e", "0x123", "123L", 0};
+
+       for (int i=0; bad_values[i]; i++) {
+               sprintf(source, "\
+{\n\
+  user(id: %s) {\n\
+    name\n\
+  }\n\
+}\n\
+               ", bad_values[i]);
+
+               // Test the lexer.
+               TEST_CONT(graphql_lex(NULL, source, &tokens) != NULL);
+               TEST_ABRT(listlen(tokens) == 5);
+               tokens = tal_free(tokens);
+
+               // No need to test parser when lexer fails.
+       }
+}
+
+void check_float_value(char *source, float float_value, const char *format) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Float Value Range Check on %f\n", float_value);
+
+       char buf[100];
+       sprintf(buf, "\
+{\n\
+  user(id: %s) {\n\
+    name\n\
+  }\n\
+}\n\
+       ", format);
+       sprintf(source, buf, float_value);
+       sprintf(buf, format, float_value);
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 11);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f');
+       TEST_STRG(tok->token_string, buf);
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 12);
+       TEST_CONT(tok->source_len == strlen(buf));
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       TEST_CONT(tok->source_line == 2);
+       TEST_CONT(tok->source_column == 12 + strlen(buf));
+       TEST_CONT(tok->source_len == 1);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_type == 'f');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_string, buf);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_valid_float_values(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Valid Float Values\n");
+
+       const char *good_values[] = {"1.0", "1e50", "6.0221413e23", "1.23", 0};
+
+       for (int i=0; good_values[i]; i++) {
+               sprintf(source, "\
+{\n\
+  user(id: %s) {\n\
+    name\n\
+  }\n\
+}\n\
+               ", good_values[i]);
+
+               // Test the lexer.
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_ABRT(listlen(tokens) == 11);
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f');
+               TEST_STRG(tok->token_string, good_values[i]);
+               TEST_CONT(tok->source_line == 2);
+               TEST_CONT(tok->source_column == 12);
+               TEST_CONT(tok->source_len == strlen(good_values[i]));
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+               TEST_CONT(tok->source_line == 2);
+               TEST_CONT(tok->source_column == 12 + strlen(good_values[i]));
+               TEST_CONT(tok->source_len == 1);
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+               tokens = tal_free(tokens);
+
+               // Test the parser.
+               struct graphql_executable_document *doc;
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+               TEST_ABRT(doc != NULL);
+               TEST_ABRT(doc->first_def != NULL);
+               TEST_ABRT(doc->first_def->op_def != NULL);
+               TEST_CONT(doc->first_def->next_def == NULL);
+               TEST_CONT(doc->first_def->frag_def == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_type == 'f');
+               TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_string, good_values[i]);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+               tokens = tal_free(tokens);
+       }
+}
+
+void check_invalid_float_values(char *source) {
+       struct list_head *tokens;
+
+       if (!mute) printf("// Invalid Float Values\n");
+
+       const char *bad_values[] = {"00.0", "-00.0", "00e1", "1.23.4", "0x1.2p3", 0};
+
+       for (int i=0; bad_values[i]; i++) {
+               sprintf(source, "\
+{\n\
+  user(id: %s) {\n\
+    name\n\
+  }\n\
+}\n\
+               ", bad_values[i]);
+
+               // Test the lexer.
+               TEST_CONT(graphql_lex(NULL, source, &tokens) != NULL);
+               TEST_ABRT(listlen(tokens) == 5);
+               tokens = tal_free(tokens);
+
+               // No need to test parser when lexer fails.
+       }
+}
+
+void check_boolean_values(char *source) {
+       struct list_head *tokens;
+
+       if (!mute) printf("// Boolean Values\n");
+
+       const char *good_values[] = {"true", "false", 0};
+
+       for (int i=0; good_values[i]; i++) {
+               sprintf(source, "\
+{\n\
+  user(id: %s) {\n\
+    name\n\
+  }\n\
+}\n\
+               ", good_values[i]);
+
+               // Test the lexer.
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_ABRT(listlen(tokens) == 11);
+               tokens = tal_free(tokens);
+
+               // Test the parser.
+               struct graphql_executable_document *doc;
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+               TEST_ABRT(doc != NULL);
+               TEST_ABRT(doc->first_def != NULL);
+               TEST_ABRT(doc->first_def->op_def != NULL);
+               TEST_CONT(doc->first_def->next_def == NULL);
+               TEST_CONT(doc->first_def->frag_def == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val->token_type == 'a');
+               TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val->token_string, good_values[i]);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+               tokens = tal_free(tokens);
+       }
+
+       const char *bad_values[] = {"True", "False", "TRUE", "FALSE", 0};
+
+       for (int i=0; bad_values[i]; i++) {
+               sprintf(source, "\
+{\n\
+  user(id: %s) {\n\
+    name\n\
+  }\n\
+}\n\
+               ", bad_values[i]);
+
+               // Test the lexer.
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_ABRT(listlen(tokens) == 11);
+               tokens = tal_free(tokens);
+
+               // Test the parser (it will succeed in parsing the bad values as enum values, not boolean values).
+               struct graphql_executable_document *doc;
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+               TEST_ABRT(doc != NULL);
+               TEST_ABRT(doc->first_def != NULL);
+               TEST_ABRT(doc->first_def->op_def != NULL);
+               TEST_CONT(doc->first_def->next_def == NULL);
+               TEST_CONT(doc->first_def->frag_def == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val->token_type == 'a');
+               TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val->token_string, bad_values[i]);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+               tokens = tal_free(tokens);
+       }
+}
+
+void check_string_value(char *source, const char *test_value, const char *expected_result) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// String Value Test: %s\n", test_value);
+
+       sprintf(source, "\
+{\n\
+  user(id:%s) {\n\
+    name\n\
+  }\n\
+}\n\
+       ", test_value);
+
+       bool block = (test_value[0]=='\"' && test_value[1]=='\"' && test_value[2]=='\"')? true: false;
+       if (expected_result) {
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_ABRT(listlen(tokens) == 11);
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+               TEST_STRG(tok->token_string, expected_result);
+               TEST_CONT(tok->source_line == 2);
+               TEST_CONT(tok->source_column == 11 + (block? 3: 1));
+               TEST_CONT(tok->source_len == strlen(test_value) - (block? 6: 2));
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+               TEST_CONT(tok->source_line == 2);
+               TEST_CONT(tok->source_column == 11 + strlen(test_value));
+               TEST_CONT(tok->source_len == 1);
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+               tokens = tal_free(tokens);
+
+               // Test the parser (it will succeed in parsing the bad values as enum values, not boolean values).
+               struct graphql_executable_document *doc;
+               TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+               TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+               TEST_ABRT(doc != NULL);
+               TEST_ABRT(doc->first_def != NULL);
+               TEST_ABRT(doc->first_def->op_def != NULL);
+               TEST_CONT(doc->first_def->next_def == NULL);
+               TEST_CONT(doc->first_def->frag_def == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val->token_type == 's');
+               TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val->token_string, expected_result);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+               TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL);
+               TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+               tokens = tal_free(tokens);
+       } else {
+               TEST_CONT(graphql_lex(NULL, source, &tokens) != NULL);
+               tokens = tal_free(tokens);
+       }
+}
+
+void check_example_25_and_26(const char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 25 and 26\n");
+
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       while ((tok = list_pop(tokens, struct graphql_token, node)) && tok->token_type != 's') { }
+       if (tok) {
+               TEST_STRG(tok->token_string, "Hello,\n  World!\n\nYours,\n  GraphQL.");
+       }
+
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_29(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 29\n");
+
+       sprintf(source, "\
+{\n\
+  field(arg: null)\n\
+  field\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 9);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "null");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "field");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "arg");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val->token_string, "null");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->next->field->name->token_string, "field");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->args == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_30_and_31(const char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 30 and 31\n");
+
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 15);
+       while ((tok = list_pop(tokens, struct graphql_token, node)) && !(tok->token_type == 'a' && tok->token_string != NULL && streq(tok->token_string, "lat"))) { }
+       TEST_CONT(tok);
+       if (tok) {
+               TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f');
+               TEST_STRG(tok->token_string, "-53.211");
+       }
+       tokens = tal_free(tokens);
+
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       while ((tok = list_pop(tokens, struct graphql_token, node)) && !(tok->token_type == 'a' && tok->token_string != NULL && streq(tok->token_string, "lon"))) { }
+       TEST_CONT(tok);
+       if (tok) {
+               TEST_CONT(tok->token_type == 'a');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+               tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f');
+               TEST_STRG(tok->token_string, "12.43");
+       }
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       const char *e;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL);
+       if (e) printf("%s\n", e);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type == NULL);
+       TEST_CONT(doc->first_def->op_def->op_name == NULL);
+       TEST_CONT(doc->first_def->op_def->vars == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "nearestThing");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "location");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->name->token_type == 'a');
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val->val->token_type == 'f');
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->name->token_type == 'a');
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val->val->token_type == 'f');
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->next == NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_32(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 32\n");
+
+       sprintf(source, "\
+query getZuckProfile($devicePicSize: Int) {\n\
+  user(id: 4) {\n\
+    id\n\
+    name\n\
+    profilePic(size: $devicePicSize)\n\
+  }\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 27);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // Test the parser.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(graphql_parse(tokens, &doc) == NULL);
+       TEST_ABRT(doc != NULL);
+       TEST_ABRT(doc->first_def != NULL);
+       TEST_CONT(doc->first_def->next_def == NULL);
+       TEST_CONT(doc->first_def->frag_def == NULL);
+       TEST_ABRT(doc->first_def->op_def != NULL);
+       TEST_ABRT(doc->first_def->op_def->op_type != NULL);
+       TEST_ABRT(doc->first_def->op_def->op_type->op_type != NULL);
+       TEST_CONT(doc->first_def->op_def->op_type->op_type->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->op_type->op_type->token_string, "query");
+       TEST_CONT(doc->first_def->op_def->op_name != NULL);
+       TEST_CONT(doc->first_def->op_def->op_name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->op_name->token_string, "getZuckProfile");
+       TEST_ABRT(doc->first_def->op_def->vars != NULL);
+       TEST_ABRT(doc->first_def->op_def->vars->first != NULL);
+       TEST_CONT(doc->first_def->op_def->vars->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->vars->first->var != NULL);
+       TEST_ABRT(doc->first_def->op_def->vars->first->var->name != NULL);
+       TEST_CONT(doc->first_def->op_def->vars->first->var->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->vars->first->var->name->token_string, "devicePicSize");
+       TEST_ABRT(doc->first_def->op_def->vars->first->type != NULL);
+//     TEST_CONT(doc->first_def->op_def->vars->first->type->list == NULL);
+//     TEST_CONT(doc->first_def->op_def->vars->first->type->non_null == NULL);
+       TEST_ABRT(doc->first_def->op_def->vars->first->type->named != NULL);
+       TEST_ABRT(doc->first_def->op_def->vars->first->type->named->name != NULL);
+       TEST_CONT(doc->first_def->op_def->vars->first->type->named->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->vars->first->type->named->name->token_string, "Int");
+       TEST_CONT(doc->first_def->op_def->vars->first->default_val == NULL);
+       TEST_CONT(doc->first_def->op_def->vars->first->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic");
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size");
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var != NULL);
+       TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name != NULL);
+       TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name->token_type == 'a');
+       TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name->token_string, "devicePicSize");
+       tokens = tal_free(tokens);
+}
+
+void check_example_34(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 34\n");
+
+       sprintf(source, "\
+type Person\n\
+  @addExternalFields(source: \"profiles\")\n\
+  @excludeField(name: \"photo\") {\n\
+  name: String\n\
+}\n\
+       ");
+
+       // Test the lexer.
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 21);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "addExternalFields");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "excludeField");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // The type system is not yet implemented, so parsing will fail here.
+       // This could be "phase 2" of this project.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT(graphql_parse(tokens, &doc) != NULL);
+       tokens = tal_free(tokens);
+}
+
+void check_example_35(char *source) {
+       struct list_head *tokens;
+       struct graphql_token *tok;
+
+       if (!mute) printf("// Example No. 35\n");
+
+       sprintf(source, "\
+type Person\n\
+  @excludeField(name: \"photo\")\n\
+  @addExternalFields(source: \"profiles\") {\n\
+  name: String\n\
+}\n\
+       ");
+
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_ABRT(listlen(tokens) == 21);
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "excludeField");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       TEST_STRG(tok->token_string, "addExternalFields");
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '(');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}');
+       tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL);
+       tokens = tal_free(tokens);
+
+       // The type system is not yet implemented, so parsing will fail here.
+       // This could be "phase 2" of this project.
+       struct graphql_executable_document *doc;
+       TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL);
+       TEST_CONT(graphql_parse(tokens, &doc) != NULL);
+       tokens = tal_free(tokens);
+}
+
+/* End of test case functions. */
+
+/* Beginning of main() test run to run all test cases. */
+
+int main(void)
+{
+       printf("\nTesting GraphQL lexer/parser...\n");
+
+       char source[1024];
+       int prev_fail; // Used by RUNx macros.
+
+       // Check the lexer with all valid line terminators.
+       const char *new_line = "\n";
+       const char *carriage_return = "\r";
+       const char *carriage_return_new_line = "\r\n";
+       const char *line_terminators[] = { new_line, carriage_return, carriage_return_new_line };
+       for (int i=0; i<3; i++) {
+               sprintf(source, "\
+{%s\
+  user(id: 4) {%s\
+    name%s\
+  }%s\
+}%s\
+               ", line_terminators[i], line_terminators[i], line_terminators[i], line_terminators[i], line_terminators[i]);
+
+               RUN(check_example_3);
+       }
+
+       RUN(check_example_5); // Parse a mutation operation and check results.
+
+       RUN(check_example_6); // Parse an unnamed query and check results.
+
+       RUN(check_example_7); // Parse multiple fields in a selection set and check results.
+       RUN(check_example_8); // Parse complex data structure and check results.
+       RUN(check_example_9); // Parse example of top-level fields and check results.
+       RUN(check_example_10); // Parse example with parameterized field and check results.
+       RUN(check_example_11); // Parse example with multiple field arguments and check results.
+
+       // Parse examples of different parameter order and check for identical results.
+       sprintf(source, "\
+{\n\
+  picture(width: 200, height: 100)\n\
+}\n\
+       ");
+       RUN(check_example_12_and_13);
+       sprintf(source, "\
+{\n\
+  picture(height: 100, width: 200)\n\
+}\n\
+       ");
+       RUN(check_example_12_and_13);
+
+       RUN(check_example_14); // Parse alias example and check results.
+       RUN(check_example_16); // Parse a top-level-field alias example and check results.
+
+       RUN(check_example_18); // Parse example and check results.
+
+       RUN(check_example_19); // Parse fragment example and check results.
+       RUN(check_example_20); // Parse another fragment example and check results.
+       RUN(check_example_21); // Parse fragment typing example and check results.
+       RUN(check_example_23); // Parse fragment typing example and check results.
+       RUN(check_example_24); // Parse fragment typing example and check results.
+
+       // Parse various int values and check results.
+       for (int i=        -15; i<=         15; i++) { RUN1(check_int_value, i); }
+       for (int i=     -32770; i<=     -32765; i++) { RUN1(check_int_value, i); }
+       for (int i=      32765; i<=      32770; i++) { RUN1(check_int_value, i); }
+       for (int i=-2147483648; i<=-2147483645; i++) { RUN1(check_int_value, i); }
+       for (int i= 2147483647; i>= 2147483645; i--) { RUN1(check_int_value, i); }
+       RUN(check_invalid_int_values);
+
+       // Parse various float values and check results.
+       for (float i=   -1.0; i<=    1.0; i+=    0.1) { RUN2(check_float_value, i, "%1.1f"); }
+       for (float i=-327.70; i<=-327.65; i+=   0.01) { RUN2(check_float_value, i, "%1.2f"); }
+       for (float i= 327.65; i<= 327.70; i+=   0.01) { RUN2(check_float_value, i, "%1.2f"); }
+       for (float i= -5e-20; i<= -1e-20; i+=  1e-20) { RUN2(check_float_value, i, "%1.0e"); }
+       for (float i=  5e-20; i>=  1e-20; i-=  1e-20) { RUN2(check_float_value, i, "%1.0e"); }
+       for (float i=  5E+20; i>=  1E+20; i-=  1E+20) { RUN2(check_float_value, i, "%1.2E"); }
+       for (float i=1.5E+20; i>=1.1E+20; i-=0.1E+20) { RUN2(check_float_value, i, "%1.1E"); }
+       RUN(check_valid_float_values);
+       RUN(check_invalid_float_values);
+
+       RUN(check_boolean_values); // Parse boolean values and check results.
+
+       // Parse various string values and check results.
+       RUN2(check_string_value, "te^st",                    NULL         ); // Missing quotes (the caret makes it an invalid token for testing purposes).
+       RUN2(check_string_value, "\"te^st\"",                "te^st"      ); // A valid string.
+       RUN2(check_string_value, "\"\"",                     ""           ); // An empty string is valid.
+       RUN2(check_string_value, "\"\"\"te^st\"\"\"",        "te^st"      ); // A block string.
+       RUN2(check_string_value, "\"te\\st\"",               NULL         ); // Backslashes are normally invalid.
+       RUN2(check_string_value, "\"te\nst\"",               NULL         ); // New-line characters are invalid except in block strings.
+       RUN2(check_string_value, "\"te\rst\"",               NULL         ); // New-line characters are invalid except in block strings.
+       RUN2(check_string_value, "\"\"\"te\nst\"\"\"",       "te\nst"     ); // New-line characters are valid in block strings.
+       RUN2(check_string_value, "\"\"\"te\rst\"\"\"",       "te\rst"     ); // New-line characters are valid in block strings.
+       RUN2(check_string_value, "\"te\"st\"",               NULL         ); // A quote in a string is invalid.
+       RUN2(check_string_value, "\"te\\\"st\"",             "te\"st"     ); // ...unless it is escaped.
+       RUN2(check_string_value, "\"\"\"te\"st\"\"\"",       "te\"st"     ); // A quote in a block string is valid.
+       RUN2(check_string_value, "\"\"\"te\"\"st\"\"\"",     "te\"\"st"   ); // It is even valid to have two quotes in a block string.
+       RUN2(check_string_value, "\"\"\"te\"\"\"st\"\"\"",   NULL         ); // Three quotes in a row are not allowed in a block string.
+       RUN2(check_string_value, "\"\"\"te\\\"\"\"st\"\"\"", "te\"\"\"st" ); // ...unless escaped.
+       RUN2(check_string_value, "\"te\\\"st\"",             "te\"st"     ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\\\st\"",             "te\\st"     ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\/st\"",              "te/st"      ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\bst\"",              "te\bst"     ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\fst\"",              "te\fst"     ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\nst\"",              "te\nst"     ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\rst\"",              "te\rst"     ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\tst\"",              "te\tst"     ); // Check escape sequence.
+       RUN2(check_string_value, "\"te\\vst\"",              NULL         ); // Invalid escape sequence.
+       RUN2(check_string_value, "\"te\\033st\"",            NULL         ); // Invalid escape sequence.
+       // Note: Unicode excape sequence is tested below.
+
+       // This block string and this string should result in identical tokens.
+       sprintf(source, "\
+mutation {\n\
+  sendEmail(message: \"\"\"\n\
+    Hello,\n\
+      World!\n\
+\n\
+    Yours,\n\
+      GraphQL.\n\
+  \"\"\")\n\
+}\n\
+       ");
+       RUN(check_example_25_and_26);
+       sprintf(source, "\
+mutation {\n\
+  sendEmail(message: \"Hello,\\n  World!\\n\\nYours,\\n  GraphQL.\")\n\
+}\n\
+       ");
+       RUN(check_example_25_and_26);
+
+       // Check block string example.
+       RUN2(check_string_value,
+"\"\"\"\n\
+This starts with and ends with an empty line,\n\
+which makes it easier to read.\n\
+\"\"\"",
+               "This starts with and ends with an empty line,\nwhich makes it easier to read.");
+
+       // Check block string counter example.
+       RUN2(check_string_value,
+"\"\"\"This does not start with or end with any empty lines,\n\
+which makes it a little harder to read.\"\"\"",
+               "This does not start with or end with any empty lines,\nwhich makes it a little harder to read.");
+
+       RUN2(check_string_value, "\"te\\u001bst\"",         "te\033st"        ); // Check unicode escape sequence.
+       RUN2(check_string_value, "\"te\\u001Bst\"",         "te\033st"        ); // Check again with other case.
+       RUN2(check_string_value, "\"\"\"te\\u001bst\"\"\"", "te\\u001bst"     ); // Escape sequences are ignored in block strings (except for the triple quote).
+       RUN2(check_string_value, "\"\"\"te\\nst\"\"\"",     "te\\nst"         ); // Escape sequences are ignored in block strings (except for the triple quote).
+       RUN2(check_string_value, "\"te\\u2026st\"",         "te\xe2\x80\xa6st"); // Check a unicode escape sequence.
+
+       RUN(check_example_29); // Parse null value and check result.
+
+       // These two input objects should have the same result.
+       sprintf(source, "\
+{\n\
+  nearestThing(location: { lon: 12.43, lat: -53.211 })\n\
+}\n\
+       ");
+       RUN(check_example_30_and_31);
+       sprintf(source, "\
+{\n\
+  nearestThing(location: { lat: -53.211, lon: 12.43 })\n\
+}\n\
+       ");
+       RUN(check_example_30_and_31);
+
+       RUN(check_example_32); // Parse an example with a variable and check result.
+
+       RUN(check_example_34); // Parse directives and check result.
+       RUN(check_example_35); // Parse directives and check result.
+
+       RUN(check_example_35); // Parse directives and check result.
+
+       printf("total passed: %d\n%stotal failed: %d\033[0m\n", pass, fail?"\033[91m":"", fail);
+
+       return fail==0? 0: 1;
+}
+
index cffd0619d338d8146365abd3f10e1b308ef4c4d4..f631ffebf1f7540416a050c800bcf5cd42268470 100644 (file)
@@ -86,14 +86,16 @@ void htable_init(struct htable *ht,
        ht->table = &ht->common_bits;
 }
 
        ht->table = &ht->common_bits;
 }
 
+/* Fill to 87.5% */
 static inline size_t ht_max(const struct htable *ht)
 {
 static inline size_t ht_max(const struct htable *ht)
 {
-       return ((size_t)3 << ht->bits) / 4;
+       return ((size_t)7 << ht->bits) / 8;
 }
 
 }
 
-static inline size_t ht_max_with_deleted(const struct htable *ht)
+/* Clean deleted if we're full, and more than 12.5% deleted */
+static inline size_t ht_max_deleted(const struct htable *ht)
 {
 {
-       return ((size_t)9 << ht->bits) / 10;
+       return ((size_t)1 << ht->bits) / 8;
 }
 
 bool htable_init_sized(struct htable *ht,
 }
 
 bool htable_init_sized(struct htable *ht,
@@ -103,7 +105,7 @@ bool htable_init_sized(struct htable *ht,
        htable_init(ht, rehash, priv);
 
        /* Don't go insane with sizing. */
        htable_init(ht, rehash, priv);
 
        /* Don't go insane with sizing. */
-       for (ht->bits = 1; ((size_t)3 << ht->bits) / 4 < expect; ht->bits++) {
+       for (ht->bits = 1; ht_max(ht) < expect; ht->bits++) {
                if (ht->bits == 30)
                        break;
        }
                if (ht->bits == 30)
                        break;
        }
@@ -195,12 +197,83 @@ void *htable_prev_(const struct htable *ht, struct htable_iter *i)
        for (;;) {
                if (!i->off)
                        return NULL;
        for (;;) {
                if (!i->off)
                        return NULL;
-               i->off --;
+               i->off--;
                if (entry_is_valid(ht->table[i->off]))
                        return get_raw_ptr(ht, ht->table[i->off]);
        }
 }
 
                if (entry_is_valid(ht->table[i->off]))
                        return get_raw_ptr(ht, ht->table[i->off]);
        }
 }
 
+/* Another bit currently in mask needs to be exposed, so that a bucket with p in
+ * it won't appear invalid */
+static COLD void unset_another_common_bit(struct htable *ht,
+                                         uintptr_t *maskdiff,
+                                         const void *p)
+{
+       size_t i;
+
+       for (i = sizeof(uintptr_t) * CHAR_BIT - 1; i > 0; i--) {
+               if (((uintptr_t)p & ((uintptr_t)1 << i))
+                   && ht->common_mask & ~*maskdiff & ((uintptr_t)1 << i))
+                       break;
+       }
+       /* There must have been one, right? */
+       assert(i > 0);
+
+       *maskdiff |= ((uintptr_t)1 << i);
+}
+
+/* We want to change the common mask: this fixes up the table */
+static COLD void fixup_table_common(struct htable *ht, uintptr_t maskdiff)
+{
+       size_t i;
+       uintptr_t bitsdiff;
+
+again:
+       bitsdiff = ht->common_bits & maskdiff;
+
+       for (i = 0; i < (size_t)1 << ht->bits; i++) {
+               uintptr_t e;
+               if (!entry_is_valid(e = ht->table[i]))
+                       continue;
+
+               /* Clear the bits no longer in the mask, set them as
+                * expected. */
+               e &= ~maskdiff;
+               e |= bitsdiff;
+               /* If this made it invalid, restart with more exposed */
+               if (!entry_is_valid(e)) {
+                       unset_another_common_bit(ht, &maskdiff, get_raw_ptr(ht, e));
+                       goto again;
+               }
+               ht->table[i] = e;
+       }
+
+       /* Take away those bits from our mask, bits and perfect bit. */
+       ht->common_mask &= ~maskdiff;
+       ht->common_bits &= ~maskdiff;
+       if (ht_perfect_mask(ht) & maskdiff)
+               ht->perfect_bitnum = NO_PERFECT_BIT;
+}
+
+/* Limited recursion */
+static void ht_add(struct htable *ht, const void *new, size_t h);
+
+/* We tried to add this entry, but it looked invalid!  We need to
+ * let another pointer bit through mask */
+static COLD void update_common_fix_invalid(struct htable *ht, const void *p, size_t h)
+{
+       uintptr_t maskdiff;
+
+       assert(ht->elems != 0);
+
+       maskdiff = 0;
+       unset_another_common_bit(ht, &maskdiff, p);
+       fixup_table_common(ht, maskdiff);
+
+       /* Now won't recurse */
+       ht_add(ht, p, h);
+}
+
 /* This does not expand the hash table, that's up to caller. */
 static void ht_add(struct htable *ht, const void *new, size_t h)
 {
 /* This does not expand the hash table, that's up to caller. */
 static void ht_add(struct htable *ht, const void *new, size_t h)
 {
@@ -214,6 +287,8 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
                i = (i + 1) & ((1 << ht->bits)-1);
        }
        ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
                i = (i + 1) & ((1 << ht->bits)-1);
        }
        ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
+       if (!entry_is_valid(ht->table[i]))
+               update_common_fix_invalid(ht, new, h);
 }
 
 static COLD bool double_table(struct htable *ht)
 }
 
 static COLD bool double_table(struct htable *ht)
@@ -283,20 +358,10 @@ static COLD void rehash_table(struct htable *ht)
 /* We stole some bits, now we need to put them back... */
 static COLD void update_common(struct htable *ht, const void *p)
 {
 /* We stole some bits, now we need to put them back... */
 static COLD void update_common(struct htable *ht, const void *p)
 {
-       unsigned int i;
-       uintptr_t maskdiff, bitsdiff;
+       uintptr_t maskdiff;
 
        if (ht->elems == 0) {
 
        if (ht->elems == 0) {
-               /* Always reveal one bit of the pointer in the bucket,
-                * so it's not zero or HTABLE_DELETED (1), even if
-                * hash happens to be 0.  Assumes (void *)1 is not a
-                * valid pointer. */
-               for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) {
-                       if ((uintptr_t)p & ((uintptr_t)1 << i))
-                               break;
-               }
-
-               ht->common_mask = ~((uintptr_t)1 << i);
+               ht->common_mask = -1;
                ht->common_bits = ((uintptr_t)p & ht->common_mask);
                ht->perfect_bitnum = 0;
                (void)htable_debug(ht, HTABLE_LOC);
                ht->common_bits = ((uintptr_t)p & ht->common_mask);
                ht->perfect_bitnum = 0;
                (void)htable_debug(ht, HTABLE_LOC);
@@ -306,33 +371,25 @@ static COLD void update_common(struct htable *ht, const void *p)
        /* Find bits which are unequal to old common set. */
        maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask);
 
        /* Find bits which are unequal to old common set. */
        maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask);
 
-       /* These are the bits which go there in existing entries. */
-       bitsdiff = ht->common_bits & maskdiff;
-
-       for (i = 0; i < (size_t)1 << ht->bits; i++) {
-               if (!entry_is_valid(ht->table[i]))
-                       continue;
-               /* Clear the bits no longer in the mask, set them as
-                * expected. */
-               ht->table[i] &= ~maskdiff;
-               ht->table[i] |= bitsdiff;
-       }
-
-       /* Take away those bits from our mask, bits and perfect bit. */
-       ht->common_mask &= ~maskdiff;
-       ht->common_bits &= ~maskdiff;
-       if (ht_perfect_mask(ht) & maskdiff)
-               ht->perfect_bitnum = NO_PERFECT_BIT;
+       fixup_table_common(ht, maskdiff);
        (void)htable_debug(ht, HTABLE_LOC);
 }
 
 bool htable_add_(struct htable *ht, size_t hash, const void *p)
 {
        (void)htable_debug(ht, HTABLE_LOC);
 }
 
 bool htable_add_(struct htable *ht, size_t hash, const void *p)
 {
-       if (ht->elems+1 > ht_max(ht) && !double_table(ht))
-               return false;
-       if (ht->elems+1 + ht->deleted > ht_max_with_deleted(ht))
-               rehash_table(ht);
+       /* Cannot insert NULL, or (void *)1. */
        assert(p);
        assert(p);
+       assert(entry_is_valid((uintptr_t)p));
+
+       /* Getting too full? */
+       if (ht->elems+1 + ht->deleted > ht_max(ht)) {
+               /* If we're more than 1/8 deleted, clean those,
+                * otherwise double table size. */
+               if (ht->deleted > ht_max_deleted(ht))
+                       rehash_table(ht);
+               else if (!double_table(ht))
+                       return false;
+       }
        if (((uintptr_t)p & ht->common_mask) != ht->common_bits)
                update_common(ht, p);
 
        if (((uintptr_t)p & ht->common_mask) != ht->common_bits)
                update_common(ht, p);
 
@@ -361,8 +418,12 @@ void htable_delval_(struct htable *ht, struct htable_iter *i)
        assert(entry_is_valid(ht->table[i->off]));
 
        ht->elems--;
        assert(entry_is_valid(ht->table[i->off]));
 
        ht->elems--;
-       ht->table[i->off] = HTABLE_DELETED;
-       ht->deleted++;
+       /* Cheap test: if the next bucket is empty, don't need delete marker */
+       if (ht->table[hash_bucket(ht, i->off+1)] != 0) {
+               ht->table[i->off] = HTABLE_DELETED;
+               ht->deleted++;
+       } else
+               ht->table[i->off] = 0;
 }
 
 void *htable_pick_(const struct htable *ht, size_t seed, struct htable_iter *i)
 }
 
 void *htable_pick_(const struct htable *ht, size_t seed, struct htable_iter *i)
index eac57e37a326612226f6cd7907c04bcbc589c360..faaf541bd8ce8c5ee5f5ea64eab3649e1462fade 100644 (file)
@@ -132,7 +132,7 @@ bool htable_copy_(struct htable *dst, const struct htable *src);
  * htable_add - add a pointer into a hash table.
  * @ht: the htable
  * @hash: the hash value of the object
  * htable_add - add a pointer into a hash table.
  * @ht: the htable
  * @hash: the hash value of the object
- * @p: the non-NULL pointer
+ * @p: the non-NULL pointer (also cannot be (void *)1).
  *
  * Also note that this can only fail due to allocation failure.  Otherwise, it
  * returns true.
  *
  * Also note that this can only fail due to allocation failure.  Otherwise, it
  * returns true.
index de4cd471404f1d4f22a4e184366a0efe9934da7c..0aacb7f334925ffe58d2c8cb489a9ae0efe36b66 100644 (file)
@@ -35,6 +35,9 @@
  *     bool <name>_del(struct <name> *ht, const <type> *e);
  *     bool <name>_delkey(struct <name> *ht, const <keytype> *k);
  *
  *     bool <name>_del(struct <name> *ht, const <type> *e);
  *     bool <name>_delkey(struct <name> *ht, const <keytype> *k);
  *
+ * Delete by iterator:
+ *     bool <name>_delval(struct <name> *ht, struct <name>_iter *i);
+ *
  * Find and return the (first) matching element, or NULL:
  *     type *<name>_get(const struct @name *ht, const <keytype> *k);
  *
  * Find and return the (first) matching element, or NULL:
  *     type *<name>_get(const struct @name *ht, const <keytype> *k);
  *
                        return name##_del(ht, elem);                    \
                return false;                                           \
        }                                                               \
                        return name##_del(ht, elem);                    \
                return false;                                           \
        }                                                               \
-       static inline UNNEEDED bool name##_pick(const struct name *ht,  \
+       static inline UNNEEDED void name##_delval(struct name *ht,      \
+                                                 struct name##_iter *iter) \
+       {                                                               \
+               htable_delval(&ht->raw, &iter->i);                      \
+       }                                                               \
+       static inline UNNEEDED type *name##_pick(const struct name *ht, \
                                                size_t seed,            \
                                                struct name##_iter *iter) \
        {                                                               \
                                                size_t seed,            \
                                                struct name##_iter *iter) \
        {                                                               \
-               /* Note &iter->i == NULL iff iter is NULL */            \
-               return htable_pick(&ht->raw, seed, &iter->i);                   \
+               return htable_pick(&ht->raw, seed, iter ? &iter->i : NULL); \
        }                                                               \
        static inline UNNEEDED type *name##_first(const struct name *ht, \
                                         struct name##_iter *iter)      \
        }                                                               \
        static inline UNNEEDED type *name##_first(const struct name *ht, \
                                         struct name##_iter *iter)      \
diff --git a/ccan/htable/test/run-clash.c b/ccan/htable/test/run-clash.c
new file mode 100644 (file)
index 0000000..a4e5d38
--- /dev/null
@@ -0,0 +1,31 @@
+#include <ccan/htable/htable.h>
+#include <ccan/htable/htable.c>
+#include <ccan/tap/tap.h>
+#include <stdbool.h>
+#include <string.h>
+
+/* Clashy hash */
+static size_t hash(const void *elem, void *unused UNNEEDED)
+{
+       return 0;
+}
+
+int main(void)
+{
+       struct htable ht;
+
+       plan_tests(254 * 253);
+       /* We try to get two elements which clash */
+       for (size_t i = 2; i < 256; i++) {
+               for (size_t j = 2; j < 256; j++) {
+                       if (i == j)
+                               continue;
+                       htable_init(&ht, hash, NULL);
+                       htable_add(&ht, hash((void *)i, NULL), (void *)i);
+                       htable_add(&ht, hash((void *)j, NULL), (void *)j);
+                       ok1(htable_check(&ht, "test"));
+                       htable_clear(&ht);
+               }
+       }
+       return exit_status();
+}
index b9f48ee70aa96afc0dec95d98d8240b2659bba17..399910354da21675f8767525a763d7bc44abfb14 100644 (file)
@@ -107,7 +107,7 @@ static bool check_mask(struct htable *ht, uint64_t val[], unsigned num)
 
 int main(void)
 {
 
 int main(void)
 {
-       unsigned int i, weight;
+       unsigned int i;
        uintptr_t perfect_bit;
        struct htable ht;
        uint64_t val[NUM_VALS];
        uintptr_t perfect_bit;
        struct htable ht;
        uint64_t val[NUM_VALS];
@@ -131,14 +131,7 @@ int main(void)
        add_vals(&ht, val, 0, 1);
        ok1(ht.bits == 1);
        ok1(ht_max(&ht) == 1);
        add_vals(&ht, val, 0, 1);
        ok1(ht.bits == 1);
        ok1(ht_max(&ht) == 1);
-       weight = 0;
-       for (i = 0; i < sizeof(ht.common_mask) * CHAR_BIT; i++) {
-               if (ht.common_mask & ((uintptr_t)1 << i)) {
-                       weight++;
-               }
-       }
-       /* Only one bit should be clear. */
-       ok1(weight == i-1);
+       ok1(ht.common_mask == -1);
 
        /* Mask should be set. */
        ok1(check_mask(&ht, val, 1));
 
        /* Mask should be set. */
        ok1(check_mask(&ht, val, 1));
index ada85f95a93d803d3db678e292bb2e33a23666cc..27007a41ac5f94d452ac4a91121b5a122d7f87fe 100644 (file)
@@ -97,7 +97,7 @@ static bool check_mask(struct htable *ht, uint64_t val[], unsigned num)
 
 int main(void)
 {
 
 int main(void)
 {
-       unsigned int i, weight;
+       unsigned int i;
        uintptr_t perfect_bit;
        struct htable ht;
        uint64_t val[NUM_VALS];
        uintptr_t perfect_bit;
        struct htable ht;
        uint64_t val[NUM_VALS];
@@ -122,14 +122,7 @@ int main(void)
        add_vals(&ht, val, 0, 1);
        ok1(ht.bits == 1);
        ok1(ht_max(&ht) == 1);
        add_vals(&ht, val, 0, 1);
        ok1(ht.bits == 1);
        ok1(ht_max(&ht) == 1);
-       weight = 0;
-       for (i = 0; i < sizeof(ht.common_mask) * CHAR_BIT; i++) {
-               if (ht.common_mask & ((uintptr_t)1 << i)) {
-                       weight++;
-               }
-       }
-       /* Only one bit should be clear. */
-       ok1(weight == i-1);
+       ok1(ht.common_mask == -1);
 
        /* Mask should be set. */
        ok1(check_mask(&ht, val, 1));
 
        /* Mask should be set. */
        ok1(check_mask(&ht, val, 1));
index a2cad59f0c2dd7e2edf17fd290d89c7a0112e522..c8a428a7567ca08458bfe5be6f749ea281c563b6 100644 (file)
@@ -4,9 +4,10 @@ CFLAGS=-Wall -Werror -O3 -I$(CCANDIR)
 
 CCAN_OBJS:=ccan-tal.o ccan-tal-str.o ccan-tal-grab_file.o ccan-take.o ccan-time.o ccan-str.o ccan-noerr.o ccan-list.o
 
 
 CCAN_OBJS:=ccan-tal.o ccan-tal-str.o ccan-tal-grab_file.o ccan-take.o ccan-time.o ccan-str.o ccan-noerr.o ccan-list.o
 
-all: speed stringspeed hsearchspeed
+all: speed stringspeed hsearchspeed density
 
 speed: speed.o hash.o $(CCAN_OBJS)
 
 speed: speed.o hash.o $(CCAN_OBJS)
+density: density.o hash.o $(CCAN_OBJS)
 
 speed.o: speed.c ../htable.h ../htable.c
 
 
 speed.o: speed.c ../htable.h ../htable.c
 
diff --git a/ccan/htable/tools/density.c b/ccan/htable/tools/density.c
new file mode 100644 (file)
index 0000000..5f7400b
--- /dev/null
@@ -0,0 +1,107 @@
+/* Density measurements for hashtables. */
+#include <ccan/err/err.h>
+#include <ccan/htable/htable_type.h>
+#include <ccan/htable/htable.c>
+#include <ccan/hash/hash.h>
+#include <ccan/ptrint/ptrint.h>
+#include <ccan/time/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* We don't actually hash objects: we put values in as if they were ptrs */
+static uintptr_t key(const ptrint_t *obj)
+{
+       return ptr2int(obj);
+}
+
+static size_t hash_uintptr(uintptr_t key)
+{
+       return hashl(&key, 1, 0);
+}
+
+static bool cmp(const ptrint_t *p, uintptr_t k)
+{
+       return key(p) == k;
+}
+
+HTABLE_DEFINE_TYPE(ptrint_t, key, hash_uintptr, cmp, htable_ptrint);
+
+/* Nanoseconds per operation */
+static size_t normalize(const struct timeabs *start,
+                       const struct timeabs *stop,
+                       unsigned int num)
+{
+       return time_to_nsec(time_divide(time_between(*stop, *start), num));
+}
+
+static size_t average_run(const struct htable_ptrint *ht, size_t count, size_t *longest)
+{
+       size_t i, total = 0;
+
+       *longest = 0;
+       for (i = 0; i < count; i++) {
+               size_t h = hash_uintptr(i + 2);
+               size_t run = 1;
+
+               while (get_raw_ptr(&ht->raw, ht->raw.table[h % ((size_t)1 << ht->raw.bits)]) != int2ptr(i + 2)) {
+                       h++;
+                       run++;
+               }
+               total += run;
+               if (run > *longest)
+                       *longest = run;
+       }
+       return total / count;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int i;
+       size_t num;
+       struct timeabs start, stop;
+       struct htable_ptrint ht;
+
+       if (argc != 2)
+               errx(1, "Usage: density <power-of-2-tablesize>");
+
+       num = atoi(argv[1]);
+
+       printf("Total buckets, buckets used, nanoseconds search time per element, avg run, longest run\n");
+       for (i = 1; i <= 99; i++) {
+               uintptr_t j;
+               struct htable_ptrint_iter it;
+               size_t count, avg_run, longest_run;
+               ptrint_t *p;
+
+               htable_ptrint_init_sized(&ht, num * 3 / 4);
+               assert((1 << ht.raw.bits) == num);
+
+               /* Can't put 0 or 1 in the hash table: multiply by a prime. */
+               for (j = 0; j < num * i / 100; j++) {
+                       htable_ptrint_add(&ht, int2ptr(j + 2));
+                       /* stop it from doubling! */
+                       ht.raw.elems = num / 2;
+               }
+               /* Must not have changed! */
+               assert((1 << ht.raw.bits) == num);
+
+               /* Clean cache */
+               count = 0;
+               for (p = htable_ptrint_first(&ht, &it); p; p = htable_ptrint_next(&ht, &it))
+                       count++;
+               assert(count == num * i / 100);
+               start = time_now();
+               for (j = 0; j < count; j++)
+                       assert(htable_ptrint_get(&ht, j + 2));
+               stop = time_now();
+               avg_run = average_run(&ht, count, &longest_run);
+               printf("%zu,%zu,%zu,%zu,%zu\n",
+                      num, count, normalize(&start, &stop, count), avg_run, longest_run);
+               fflush(stdout);
+               htable_ptrint_clear(&ht);
+       }
+
+       return 0;
+}
index 6e75450e67e85af287a9967cd7e88af66e147a5b..e10c17239cdfc407e40da7c77dfb4e5f57edc42a 100644 (file)
@@ -42,9 +42,9 @@
 #define MAX_LEVEL (MAX_ID_SHIFT + IDTREE_BITS - 1) / IDTREE_BITS
 #define IDTREE_FREE_MAX MAX_LEVEL + MAX_LEVEL
 
 #define MAX_LEVEL (MAX_ID_SHIFT + IDTREE_BITS - 1) / IDTREE_BITS
 #define IDTREE_FREE_MAX MAX_LEVEL + MAX_LEVEL
 
-#define set_bit(bit, v) (v) |= (1<<(bit))
-#define clear_bit(bit, v) (v) &= ~(1<<(bit))
-#define test_bit(bit, v) ((v) & (1<<(bit)))
+#define set_bit(bit, v) (v) |= (1U<<(bit))
+#define clear_bit(bit, v) (v) &= ~(1U<<(bit))
+#define test_bit(bit, v) ((v) & (1U<<(bit)))
 
 struct idtree_layer {
        uint32_t                 bitmap;
 
 struct idtree_layer {
        uint32_t                 bitmap;
@@ -122,7 +122,7 @@ restart:
                        /* no space available go back to previous layer. */
                        l++;
                        oid = id;
                        /* no space available go back to previous layer. */
                        l++;
                        oid = id;
-                       id = (id | ((1 << (IDTREE_BITS*l))-1)) + 1;
+                       id = (id | ((1U << (IDTREE_BITS*l))-1)) + 1;
 
                        /* if already at the top layer, we need to grow */
                        if (!(p = pa[l])) {
 
                        /* if already at the top layer, we need to grow */
                        if (!(p = pa[l])) {
index 9adbb8243f6c4f67da7ccefbc8fc7735f63cda7e..32702b178567e503c11d606d655738fe9dbfc3aa 100644 (file)
@@ -120,7 +120,10 @@ int ilog64_nz(uint64_t _v) CONST_FUNCTION;
 #endif
 
 #ifdef builtin_ilog32_nz
 #endif
 
 #ifdef builtin_ilog32_nz
-#define ilog32(_v) (builtin_ilog32_nz(_v)&-!!(_v))
+/* This used to be builtin_ilog32_nz(_v)&-!!(_v), which means it zeroes out
+ * the undefined builtin_ilog32_nz(0) return.  But clang UndefinedBehaviorSantizer
+ * complains, so do the branch: */
+#define ilog32(_v) ((_v) ? builtin_ilog32_nz(_v) : 0)
 #define ilog32_nz(_v) builtin_ilog32_nz(_v)
 #else
 #define ilog32_nz(_v) ilog32(_v)
 #define ilog32_nz(_v) builtin_ilog32_nz(_v)
 #else
 #define ilog32_nz(_v) ilog32(_v)
@@ -128,7 +131,7 @@ int ilog64_nz(uint64_t _v) CONST_FUNCTION;
 #endif /* builtin_ilog32_nz */
 
 #ifdef builtin_ilog64_nz
 #endif /* builtin_ilog32_nz */
 
 #ifdef builtin_ilog64_nz
-#define ilog64(_v) (builtin_ilog64_nz(_v)&-!!(_v))
+#define ilog32(_v) ((_v) ? builtin_ilog32_nz(_v) : 0)
 #define ilog64_nz(_v) builtin_ilog64_nz(_v)
 #else
 #define ilog64_nz(_v) ilog64(_v)
 #define ilog64_nz(_v) builtin_ilog64_nz(_v)
 #else
 #define ilog64_nz(_v) ilog64(_v)
index ba09025aaf8e5bed001debd89a018f9385415910..0b10e8a8bbf489bb2ddcf6223d27ece866240610 100644 (file)
  *                                     read_more, buf);
  *     }
  *
  *                                     read_more, buf);
  *     }
  *
+ *     // Clean up allocation so -fsanitize=address doesn't see leak!
+ *     static void free_buf(struct io_conn *c, struct buf *buf)
+ *     {
+ *             free(buf);
+ *     }
+ *
  *     // Child has received fd, start reading loop.
  *     static struct io_plan *got_infd(struct io_conn *conn, int *infd)
  *     {
  *             struct buf *buf = calloc(1, sizeof(*buf));
  *     // Child has received fd, start reading loop.
  *     static struct io_plan *got_infd(struct io_conn *conn, int *infd)
  *     {
  *             struct buf *buf = calloc(1, sizeof(*buf));
+ *             struct io_conn *new_conn;
  *
  *
- *             io_new_conn(NULL, *infd, read_more, buf);
+ *             new_conn = io_new_conn(NULL, *infd, read_more, buf);
+ *             io_set_finish(new_conn, free_buf, buf);
  *             return io_close(conn);
  *     }
  *     // Child is receiving the fd to read into. 
  *             return io_close(conn);
  *     }
  *     // Child is receiving the fd to read into. 
index 36dcb81e720f448bf46665f93bb40f0692d2256c..12df06d82cb6474fed825cd381f7f225fc6213b1 100644 (file)
@@ -529,6 +529,18 @@ bool io_plan_out_started(const struct io_conn *conn)
        return conn->plan[IO_OUT].status == IO_POLLING_STARTED;
 }
 
        return conn->plan[IO_OUT].status == IO_POLLING_STARTED;
 }
 
+/* Despite being a TCP expert, I missed the full extent of this
+ * problem.  The legendary ZmnSCPxj implemented it (with the URL
+ * pointing to the explanation), and I imitate that here. */
+struct io_plan *io_sock_shutdown(struct io_conn *conn)
+{
+       if (shutdown(io_conn_fd(conn), SHUT_WR) != 0)
+               return io_close(conn);
+
+       /* And leave unset .*/
+       return &conn->plan[IO_IN];
+}
+       
 bool io_flush_sync(struct io_conn *conn)
 {
        struct io_plan *plan = &conn->plan[IO_OUT];
 bool io_flush_sync(struct io_conn *conn)
 {
        struct io_plan *plan = &conn->plan[IO_OUT];
index e6905fb9241a78e10b56a5b63b432a99597ee185..eeb5e36ecdff0bfedd633b734cc2b380b92d8b2b 100644 (file)
@@ -389,6 +389,39 @@ struct io_plan *io_out_always_(struct io_conn *conn,
                                                       void *),
                               void *arg);
 
                                                       void *),
                               void *arg);
 
+/**
+ * io_sock_shutdown - start socket close process (flushes TCP sockets).
+ * @conn: the connection the plan is for
+ *
+ * Simply closing a TCP socket can lose data; unfortunately you should
+ * shutdown(SHUT_WR) and wait for the other side to see this and close.
+ * Of course, you also need to set a timer, in case it doesn't (you may
+ * already have some responsiveness timer, of course).
+ *
+ * On error, is equivalent to io_close().
+ *
+ * Example:
+ * #include <ccan/timer/timer.h>
+ *
+ * // Timer infra needs wrapper to contain extra data.
+ * struct timeout_timer {
+ *    struct timer t;
+ *    struct io_conn *conn;
+ * };
+ * static struct timers timers;
+ *
+ * static struct io_plan *flush_and_close(struct io_conn *conn)
+ * {
+ *    struct timeout_timer *timeout;
+ *    // Freed if conn closes normally.
+ *    timeout = tal(conn, struct timeout_timer);
+ *    timeout->conn = conn;
+ *    timer_addrel(&timers, &timeout->t, time_from_sec(5));
+ *    return io_sock_shutdown(conn);
+ * }
+ */
+struct io_plan *io_sock_shutdown(struct io_conn *conn);
+
 /**
  * io_connect - create an asynchronous connection to a listening socket.
  * @conn: the connection that plan is for.
 /**
  * io_connect - create an asynchronous connection to a listening socket.
  * @conn: the connection that plan is for.
@@ -781,4 +814,13 @@ struct timemono (*io_time_override(struct timemono (*now)(void)))(void);
  */
 int (*io_poll_override(int (*poll)(struct pollfd *fds, nfds_t nfds, int timeout)))(struct pollfd *, nfds_t, int);
 
  */
 int (*io_poll_override(int (*poll)(struct pollfd *fds, nfds_t nfds, int timeout)))(struct pollfd *, nfds_t, int);
 
+/**
+ * io_have_fd - do we own this file descriptor?
+ * @fd: the file descriptor.
+ * @listener: if non-NULL, set to true if it's a listening socket (io_listener).
+ *
+ * Returns NULL if we don't own it, otherwise a struct io_conn * or struct io_listener *.
+ */
+const void *io_have_fd(int fd, bool *listener);
+
 #endif /* CCAN_IO_H */
 #endif /* CCAN_IO_H */
index 17f9384587d92e39b37e199b0f05159bb899d112..634f83d286a66669c54694daf2fab0de4ce1a416 100644 (file)
@@ -121,7 +121,9 @@ bool add_listener(struct io_listener *l)
 
 static int find_always(const struct io_plan *plan)
 {
 
 static int find_always(const struct io_plan *plan)
 {
-       for (size_t i = 0; i < num_always; i++)
+       size_t i = 0;
+
+       for (i = 0; i < num_always; i++)
                if (always[i] == plan)
                        return i;
        return -1;
                if (always[i] == plan)
                        return i;
        return -1;
@@ -287,8 +289,10 @@ static bool *exclusive(struct io_plan *plan)
 /* For simplicity, we do one always at a time */
 static bool handle_always(void)
 {
 /* For simplicity, we do one always at a time */
 static bool handle_always(void)
 {
+       int i;
+
        /* Backwards is simple easier to remove entries */
        /* Backwards is simple easier to remove entries */
-       for (int i = num_always - 1; i >= 0; i--) {
+       for (i = num_always - 1; i >= 0; i--) {
                struct io_plan *plan = always[i];
 
                if (num_exclusive && !*exclusive(plan))
                struct io_plan *plan = always[i];
 
                if (num_exclusive && !*exclusive(plan))
@@ -323,10 +327,12 @@ bool backend_set_exclusive(struct io_plan *plan, bool excl)
  * else that we manipulate events. */
 static void exclude_pollfds(void)
 {
  * else that we manipulate events. */
 static void exclude_pollfds(void)
 {
+       size_t i;
+
        if (num_exclusive == 0)
                return;
 
        if (num_exclusive == 0)
                return;
 
-       for (size_t i = 0; i < num_fds; i++) {
+       for (i = 0; i < num_fds; i++) {
                struct pollfd *pfd = &pollfds[fds[i]->backend_info];
 
                if (!fds[i]->exclusive[IO_IN])
                struct pollfd *pfd = &pollfds[fds[i]->backend_info];
 
                if (!fds[i]->exclusive[IO_IN])
@@ -343,10 +349,12 @@ static void exclude_pollfds(void)
 
 static void restore_pollfds(void)
 {
 
 static void restore_pollfds(void)
 {
+       size_t i;
+
        if (num_exclusive == 0)
                return;
 
        if (num_exclusive == 0)
                return;
 
-       for (size_t i = 0; i < num_fds; i++) {
+       for (i = 0; i < num_fds; i++) {
                struct pollfd *pfd = &pollfds[fds[i]->backend_info];
 
                if (fds[i]->listener) {
                struct pollfd *pfd = &pollfds[fds[i]->backend_info];
 
                if (fds[i]->listener) {
@@ -456,3 +464,15 @@ void *io_loop(struct timers *timers, struct timer **expired)
 
        return ret;
 }
 
        return ret;
 }
+
+const void *io_have_fd(int fd, bool *listener)
+{
+       for (size_t i = 0; i < num_fds; i++) {
+               if (fds[i]->fd != fd)
+                       continue;
+               if (listener)
+                       *listener = fds[i]->listener;
+               return fds[i];
+       }
+       return NULL;
+}
index ed5255e682dae1ee4e5fa86e6d718810b3623059..dccb58aa19b3d36d92c95bff409b7c40328d1a13 100644 (file)
@@ -79,7 +79,7 @@ bool        json_validate       (const char *json);
 /*** Lookup and traversal ***/
 
 JsonNode   *json_find_element   (JsonNode *array, int index);
 /*** Lookup and traversal ***/
 
 JsonNode   *json_find_element   (JsonNode *array, int index);
-JsonNode   *json_find_member    (JsonNode *object, const char *key);
+JsonNode   *json_find_member    (JsonNode *object, const char *name);
 
 JsonNode   *json_first_child    (const JsonNode *node);
 
 
 JsonNode   *json_first_child    (const JsonNode *node);
 
index ed4bfab944504a48db7c9e1b4cdd2825d8863bf2..daa14abe8c8949e8f9f022a955ae3a787c9a245a 100644 (file)
@@ -21,7 +21,8 @@ bool json_escape_eq(const struct json_escape *a, const struct json_escape *b)
 
 bool json_escape_needed(const char *str, size_t len)
 {
 
 bool json_escape_needed(const char *str, size_t len)
 {
-       for (size_t i = 0; i < len; i++) {
+       size_t i;
+       for (i = 0; i < len; i++) {
                if ((unsigned)str[i] < ' '
                    || str[i] == 127
                    || str[i] == '"'
                if ((unsigned)str[i] < ' '
                    || str[i] == 127
                    || str[i] == '"'
index 87deaca85f9ce0375ff9bd299ced923669f7c739..53837e67c33a40f8b1f04aedbe856430b327fcb6 100644 (file)
@@ -337,11 +337,12 @@ char *json_out_direct(struct json_out *jout, size_t len)
        return p;
 }
 
        return p;
 }
 
-void json_out_finished(const struct json_out *jout)
+void json_out_finished(struct json_out *jout)
 {
 #ifdef CCAN_JSON_OUT_DEBUG
        assert(tal_count(jout->wrapping) == 0);
 #endif
 {
 #ifdef CCAN_JSON_OUT_DEBUG
        assert(tal_count(jout->wrapping) == 0);
 #endif
+       jout->empty = true;
 }
 
 const char *json_out_contents(const struct json_out *jout, size_t *len)
 }
 
 const char *json_out_contents(const struct json_out *jout, size_t *len)
index 2911ff2473bac5cd029eff02558c4fe6ef48bb37..da8b4ffa706206596553c391bc9c608cfcb72b6d 100644 (file)
@@ -177,9 +177,12 @@ bool json_out_add_splice(struct json_out *jout,
  * @jout: the json_out object written to.
  *
  * This simply causes internal assertions that all arrays and objects are
  * @jout: the json_out object written to.
  *
  * This simply causes internal assertions that all arrays and objects are
- * finished.  It needs CCAN_JSON_OUT_DEBUG defined to have any effect.
+ * finished. If CCAN_JSON_OUT_DEBUG is defined, it does sanity checks.
+ *
+ * This also resets the empty flag, so there will be no comma added if
+ * another JSON object is written.
  */
  */
-void json_out_finished(const struct json_out *jout);
+void json_out_finished(struct json_out *jout);
 
 /**
  * json_out_contents - read contents from json_out stream.
 
 /**
  * json_out_contents - read contents from json_out stream.
index 19f69c038c67efe1e11ac8ab5cec036123aa3b95..20286dcbefd42f99a7e88932ab1ef4cda115f2e2 100644 (file)
@@ -104,7 +104,7 @@ void *memcchr(void const *data, int c, size_t data_len);
 PURE_FUNCTION
 static inline bool memeq(const void *a, size_t al, const void *b, size_t bl)
 {
 PURE_FUNCTION
 static inline bool memeq(const void *a, size_t al, const void *b, size_t bl)
 {
-       return al == bl && !memcmp(a, b, bl);
+       return al == bl && (al == 0 || !memcmp(a, b, bl));
 }
 
 /**
 }
 
 /**
index bdcbce2b2f205724d39a6783e212eee2da3a7e83..a859318c62ee3486f5110ad9dbe7ee74e9e08438 100644 (file)
  *
  *     membuf_init(&charbuf, malloc(10), 10, membuf_realloc);
  *
  *
  *     membuf_init(&charbuf, malloc(10), 10, membuf_realloc);
  *
- *     for (int i = 1; i < argc; i++)
- *             strcpy(membuf_add(&charbuf, strlen(argv[i])), argv[i]);
+ *     for (int i = 1; i < argc; i++) {
+ *             size_t len = strlen(argv[i]);
+ *             memcpy(membuf_add(&charbuf, len), argv[i], len);
+ *     }
  *
  *     // This is dumb, we could do all at once, but shows technique.
  *     while (membuf_num_elems(&charbuf) > 0)
  *             printf("%c", *(char *)membuf_consume(&charbuf, 1));
  *     printf("\n");
  *
  *     // This is dumb, we could do all at once, but shows technique.
  *     while (membuf_num_elems(&charbuf) > 0)
  *             printf("%c", *(char *)membuf_consume(&charbuf, 1));
  *     printf("\n");
+ *     free(membuf_cleanup(&charbuf));
  *     return 0;
  * }
  */
  *     return 0;
  * }
  */
index 11c6b670aa456809f528c0eb6d905d2cb3c0b5bb..f57b8dd6ea1b4becf24763b351ea093586635a56 100644 (file)
@@ -75,7 +75,7 @@ close:
 }
 
 
 }
 
 
-int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
+int net_connect_async(const struct addrinfo *addrinfo, struct pollfd *pfds)
 {
        const struct addrinfo *addr[2] = { NULL, NULL };
        unsigned int i;
 {
        const struct addrinfo *addr[2] = { NULL, NULL };
        unsigned int i;
@@ -122,7 +122,7 @@ int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
        return -1;
 }
 
        return -1;
 }
 
-void net_connect_abort(struct pollfd pfds[2])
+void net_connect_abort(struct pollfd *pfds)
 {
        unsigned int i;
 
 {
        unsigned int i;
 
@@ -133,7 +133,7 @@ void net_connect_abort(struct pollfd pfds[2])
        }
 }
 
        }
 }
 
-int net_connect_complete(struct pollfd pfds[2])
+int net_connect_complete(struct pollfd *pfds)
 {
        unsigned int i;
 
 {
        unsigned int i;
 
index 967764e7e86cb9de97c622e9b297e74071b85907..116c2596ee622308cabc0f2268b9f74346321fa5 100644 (file)
@@ -39,6 +39,8 @@
  *                                     printf("%i,", i);
  *                     printf("\n");
  *             }
  *                                     printf("%i,", i);
  *                     printf("\n");
  *             }
+ *             // Keep -fsanitize=address leak detection happy.
+ *             objset_clear(&args);
  *             return 0;
  *     }
  *     // Given "a b c" outputs No arguments start with -.
  *             return 0;
  *     }
  *     // Given "a b c" outputs No arguments start with -.
index 9444d48c76ddf82156e503103194fd80a14aa176..03ec00f31199bb793396efc3011773b1d316d87b 100644 (file)
@@ -132,7 +132,7 @@ static inline bool objset_empty_(const struct objset_h *set)
 /**
  * objset_iter - iterator reference.
  *
 /**
  * objset_iter - iterator reference.
  *
- * This is valid for a particular set as long as the contents remain unchaged,
+ * This is valid for a particular set as long as the contents remain unchanged,
  * otherwise the effect is undefined.
  */
 struct objset_iter {
  * otherwise the effect is undefined.
  */
 struct objset_iter {
index 118e543602e06b981e6faee7dd5f416cf819d5cd..df7ee6bb1f20cf791fe82525393e7c809b6f7afb 100644 (file)
@@ -138,10 +138,11 @@ char *opt_set_floatval(const char *arg, float *f)
        return NULL;
 }
 
        return NULL;
 }
 
-void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f)
+bool opt_show_floatval(char *buf, size_t len, const float *f)
 {
        double d = *f;
 {
        double d = *f;
-       opt_show_doubleval(buf, &d);
+       opt_show_doubleval(buf, len, &d);
+       return true;
 }
 
 char *opt_set_doubleval(const char *arg, double *d)
 }
 
 char *opt_set_doubleval(const char *arg, double *d)
@@ -160,9 +161,10 @@ char *opt_set_doubleval(const char *arg, double *d)
        return NULL;
 }
 
        return NULL;
 }
 
-void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d)
+bool opt_show_doubleval(char *buf, size_t len, const double *d)
 {
 {
-       snprintf(buf, OPT_SHOW_LEN, "%f", *d);
+       snprintf(buf, len, "%f", *d);
+       return true;
 }
 
 char *opt_inc_intval(int *i)
 }
 
 char *opt_inc_intval(int *i)
@@ -196,52 +198,60 @@ char *opt_usage_and_exit(const char *extra)
        exit(0);
 }
 
        exit(0);
 }
 
-void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b)
+bool opt_show_bool(char *buf, size_t len, const bool *b)
 {
 {
-       strncpy(buf, *b ? "true" : "false", OPT_SHOW_LEN);
+       strncpy(buf, *b ? "true" : "false", len);
+       return true;
 }
 
 }
 
-void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b)
+bool opt_show_invbool(char *buf, size_t len, const bool *b)
 {
 {
-       strncpy(buf, *b ? "false" : "true", OPT_SHOW_LEN);
+       strncpy(buf, *b ? "false" : "true", len);
+       return true;
 }
 
 }
 
-void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p)
+bool opt_show_charp(char *buf, size_t len, char *const *p)
 {
 {
-       if (*p){
-               size_t len = strlen(*p);
+       if (*p) {
+               size_t plen = strlen(*p);
+               if (len < 2)
+                       return false;
                buf[0] = '"';
                buf[0] = '"';
-               if (len > OPT_SHOW_LEN - 2)
-                       len = OPT_SHOW_LEN - 2;
-               strncpy(buf+1, *p, len);
-               buf[1+len] = '"';
-               if (len < OPT_SHOW_LEN - 2)
-                       buf[2+len] = '\0';
-       }
-       else {
-               strncpy(buf, "(nil)", OPT_SHOW_LEN);
+               if (plen > len - 2)
+                       plen = len - 2;
+               strncpy(buf+1, *p, plen);
+               buf[1+plen] = '"';
+               if (plen < len - 2)
+                       buf[2+plen] = '\0';
+               return true;
+       else {
+               return false;
        }
 }
 
 /* Show an integer value, various forms. */
        }
 }
 
 /* Show an integer value, various forms. */
-void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i)
+bool opt_show_intval(char *buf, size_t len, const int *i)
 {
 {
-       snprintf(buf, OPT_SHOW_LEN, "%i", *i);
+       snprintf(buf, len, "%i", *i);
+       return true;
 }
 
 }
 
-void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui)
+bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui)
 {
 {
-       snprintf(buf, OPT_SHOW_LEN, "%u", *ui);
+       snprintf(buf, len, "%u", *ui);
+       return true;
 }
 
 }
 
-void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l)
+bool opt_show_longval(char *buf, size_t len, const long *l)
 {
 {
-       snprintf(buf, OPT_SHOW_LEN, "%li", *l);
+       snprintf(buf, len, "%li", *l);
+       return true;
 }
 
 }
 
-void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul)
+bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul)
 {
 {
-       snprintf(buf, OPT_SHOW_LEN, "%lu", *ul);
+       snprintf(buf, len, "%lu", *ul);
+       return true;
 }
 
 /* a helper function that multiplies out an argument's kMGTPE suffix in the
 }
 
 /* a helper function that multiplies out an argument's kMGTPE suffix in the
@@ -447,14 +457,14 @@ char * opt_set_uintval_si(const char *arg, unsigned int *u)
   are separate but essentially identical functions for signed and unsigned
   values, so that unsigned values greater than LLONG_MAX get suffixes.
  */
   are separate but essentially identical functions for signed and unsigned
   values, so that unsigned values greater than LLONG_MAX get suffixes.
  */
-static void show_llong_with_suffix(char buf[OPT_SHOW_LEN], long long ll,
-                                   const long long base)
+static void show_llong_with_suffix(char *buf, size_t len, long long ll,
+                                  const long long base)
 {
        const char *suffixes = "kMGTPE";
        int i;
        if (ll == 0){
                /*zero is special because everything divides it (you'd get "0E")*/
 {
        const char *suffixes = "kMGTPE";
        int i;
        if (ll == 0){
                /*zero is special because everything divides it (you'd get "0E")*/
-               snprintf(buf, OPT_SHOW_LEN, "0");
+               snprintf(buf, len, "0");
                return;
        }
        for (i = 0; i < strlen(suffixes); i++){
                return;
        }
        for (i = 0; i < strlen(suffixes); i++){
@@ -464,19 +474,20 @@ static void show_llong_with_suffix(char buf[OPT_SHOW_LEN], long long ll,
                ll = tmp;
        }
        if (i == 0)
                ll = tmp;
        }
        if (i == 0)
-               snprintf(buf, OPT_SHOW_LEN, "%"PRId64, (int64_t)ll);
+               snprintf(buf, len, "%"PRId64, (int64_t)ll);
        else
        else
-               snprintf(buf, OPT_SHOW_LEN, "%"PRId64"%c", (int64_t)ll, suffixes[i - 1]);
+               snprintf(buf, len, "%"PRId64"%c", (int64_t)ll, suffixes[i - 1]);
 }
 
 }
 
-static void show_ullong_with_suffix(char buf[OPT_SHOW_LEN], unsigned long long ull,
+static void show_ullong_with_suffix(char *buf, size_t len,
+                                   unsigned long long ull,
                                    const unsigned base)
 {
        const char *suffixes = "kMGTPE";
        int i;
        if (ull == 0){
                /*zero is special because everything divides it (you'd get "0E")*/
                                    const unsigned base)
 {
        const char *suffixes = "kMGTPE";
        int i;
        if (ull == 0){
                /*zero is special because everything divides it (you'd get "0E")*/
-               snprintf(buf, OPT_SHOW_LEN, "0");
+               snprintf(buf, len, "0");
                return;
        }
        for (i = 0; i < strlen(suffixes); i++){
                return;
        }
        for (i = 0; i < strlen(suffixes); i++){
@@ -486,72 +497,84 @@ static void show_ullong_with_suffix(char buf[OPT_SHOW_LEN], unsigned long long u
                ull = tmp;
        }
        if (i == 0)
                ull = tmp;
        }
        if (i == 0)
-               snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, (uint64_t)ull);
+               snprintf(buf, len, "%"PRIu64, (uint64_t)ull);
        else
        else
-               snprintf(buf, OPT_SHOW_LEN, "%"PRIu64"%c", (uint64_t)ull, suffixes[i - 1]);
+               snprintf(buf, len, "%"PRIu64"%c", (uint64_t)ull, suffixes[i - 1]);
 }
 
 /* _bi, signed */
 }
 
 /* _bi, signed */
-void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x)
+bool opt_show_intval_bi(char *buf, size_t len, const int *x)
 {
 {
-       show_llong_with_suffix(buf, *x, 1024);
+       show_llong_with_suffix(buf, len, *x, 1024);
+       return true;
 }
 
 }
 
-void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x)
+bool opt_show_longval_bi(char *buf, size_t len, const long *x)
 {
 {
-       show_llong_with_suffix(buf, *x, 1024);
+       show_llong_with_suffix(buf, len, *x, 1024);
+       return true;
 }
 
 }
 
-void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x)
+bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x)
 {
 {
-       show_llong_with_suffix(buf, *x, 1024);
+       show_llong_with_suffix(buf, len, *x, 1024);
+       return true;
 }
 
 /* _bi, unsigned */
 }
 
 /* _bi, unsigned */
-void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x)
+bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x)
 {
 {
-       show_ullong_with_suffix(buf, (unsigned long long) *x, 1024);
+       show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024);
+       return true;
 }
 
 }
 
-void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x)
+bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x)
 {
 {
-       show_ullong_with_suffix(buf, (unsigned long long) *x, 1024);
+       show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024);
+       return true;
 }
 
 }
 
-void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x)
+bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x)
 {
 {
-       show_ullong_with_suffix(buf, (unsigned long long) *x, 1024);
+       show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024);
+       return true;
 }
 
 /* _si, signed */
 }
 
 /* _si, signed */
-void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x)
+bool opt_show_intval_si(char *buf, size_t len, const int *x)
 {
 {
-       show_llong_with_suffix(buf, (long long) *x, 1000);
+       show_llong_with_suffix(buf, len, (long long) *x, 1000);
+       return true;
 }
 
 }
 
-void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x)
+bool opt_show_longval_si(char *buf, size_t len, const long *x)
 {
 {
-       show_llong_with_suffix(buf, (long long) *x, 1000);
+       show_llong_with_suffix(buf, len, (long long) *x, 1000);
+       return true;
 }
 
 }
 
-void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x)
+bool opt_show_longlongval_si(char *buf, size_t len, const long long *x)
 {
 {
-       show_llong_with_suffix(buf, *x, 1000);
+       show_llong_with_suffix(buf, len, *x, 1000);
+       return true;
 }
 
 /* _si, unsigned */
 }
 
 /* _si, unsigned */
-void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x)
+bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x)
 {
 {
-       show_ullong_with_suffix(buf, (unsigned long long) *x, 1000);
+       show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000);
+       return true;
 }
 
 }
 
-void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x)
+bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x)
 {
 {
-       show_ullong_with_suffix(buf, (unsigned long long) *x, 1000);
+       show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000);
+       return true;
 }
 
 }
 
-void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x)
+bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x)
 {
 {
-       show_ullong_with_suffix(buf, (unsigned long long) *x, 1000);
+       show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000);
+       return true;
 }
 
 }
 
index d376a598da932445de592c8f12e2466d4bc431bd..9149374cb001e6152eb46e03de6cc092ef6e4297 100644 (file)
@@ -34,7 +34,7 @@ static const char *next_name(const char *names, unsigned *len)
 static const char *first_opt(unsigned *i, unsigned *len)
 {
        for (*i = 0; *i < opt_count; (*i)++) {
 static const char *first_opt(unsigned *i, unsigned *len)
 {
        for (*i = 0; *i < opt_count; (*i)++) {
-               if (opt_table[*i].type == OPT_SUBTABLE)
+               if (opt_table[*i].type & OPT_SUBTABLE)
                        continue;
                return first_name(opt_table[*i].names, len);
        }
                        continue;
                return first_name(opt_table[*i].names, len);
        }
@@ -44,7 +44,7 @@ static const char *first_opt(unsigned *i, unsigned *len)
 static const char *next_opt(const char *p, unsigned *i, unsigned *len)
 {
        for (; *i < opt_count; (*i)++) {
 static const char *next_opt(const char *p, unsigned *i, unsigned *len)
 {
        for (; *i < opt_count; (*i)++) {
-               if (opt_table[*i].type == OPT_SUBTABLE)
+               if (opt_table[*i].type & OPT_SUBTABLE)
                        continue;
                if (!p)
                        return first_name(opt_table[*i].names, len);
                        continue;
                if (!p)
                        return first_name(opt_table[*i].names, len);
@@ -114,10 +114,11 @@ static void check_opt(const struct opt_table *entry)
 {
        const char *p;
        unsigned len;
 {
        const char *p;
        unsigned len;
+       enum opt_type type = entry->type & (OPT_USER_MIN-1);
 
 
-       if (entry->type != OPT_HASARG && entry->type != OPT_NOARG
-           && entry->type != (OPT_EARLY|OPT_HASARG)
-           && entry->type != (OPT_EARLY|OPT_NOARG))
+       if (type != OPT_HASARG && type != OPT_NOARG
+           && type != (OPT_EARLY|OPT_HASARG)
+           && type != (OPT_EARLY|OPT_NOARG))
                failmsg("Option %s: unknown entry type %u",
                        entry->names, entry->type);
 
                failmsg("Option %s: unknown entry type %u",
                        entry->names, entry->type);
 
@@ -161,7 +162,7 @@ static void add_opt(const struct opt_table *entry)
 void _opt_register(const char *names, enum opt_type type,
                   char *(*cb)(void *arg),
                   char *(*cb_arg)(const char *optarg, void *arg),
 void _opt_register(const char *names, enum opt_type type,
                   char *(*cb)(void *arg),
                   char *(*cb_arg)(const char *optarg, void *arg),
-                  void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
+                  bool (*show)(char *buf, size_t len, const void *arg),
                   const void *arg, const char *desc)
 {
        struct opt_table opt;
                   const void *arg, const char *desc)
 {
        struct opt_table opt;
@@ -181,7 +182,7 @@ bool opt_unregister(const char *names)
        int found = -1, i;
 
        for (i = 0; i < opt_count; i++) {
        int found = -1, i;
 
        for (i = 0; i < opt_count; i++) {
-               if (opt_table[i].type == OPT_SUBTABLE)
+               if (opt_table[i].type & OPT_SUBTABLE)
                        continue;
                if (strcmp(opt_table[i].names, names) == 0)
                        found = i;
                        continue;
                if (strcmp(opt_table[i].names, names) == 0)
                        found = i;
@@ -203,7 +204,7 @@ void opt_register_table(const struct opt_table entry[], const char *desc)
                add_opt(&heading);
        }
        for (i = 0; entry[i].type != OPT_END; i++) {
                add_opt(&heading);
        }
        for (i = 0; entry[i].type != OPT_END; i++) {
-               if (entry[i].type == OPT_SUBTABLE)
+               if (entry[i].type & OPT_SUBTABLE)
                        opt_register_table(subtable_of(&entry[i]),
                                           entry[i].desc);
                else {
                        opt_register_table(subtable_of(&entry[i]),
                                           entry[i].desc);
                else {
index 6f4b9dda8c85a4f33389863963b57f1a87214ea4..e0331be264230cb57a100f30e26a6f119bf6ca6c 100644 (file)
@@ -47,10 +47,11 @@ struct opt_table;
  * where "type" is the type of the @arg argument.  The first argument to the
  * @cb is the argument found on the commandline.
  *
  * where "type" is the type of the @arg argument.  The first argument to the
  * @cb is the argument found on the commandline.
  *
- * Similarly, if @show is not NULL, it should be of type "void *show(char *,
- * const type *)".  It should write up to OPT_SHOW_LEN bytes into the first
- * argument; unless it uses the entire OPT_SHOW_LEN bytes it should
- * nul-terminate that buffer.
+ * Similarly, if @show is not NULL, it should be of type "bool show(char *,
+ * size_t len, const type *)".  If there is no default, it should return false,
+ * otherwise it should write up to len bytes into the first argument and
+ * return true; unless it uses the entire len bytes it should nul-terminate that
+ * buffer.
  *
  * Any number of equivalent short or long options can be listed in @names,
  * separated by '|'.  Short options are a single hyphen followed by a single
  *
  * Any number of equivalent short or long options can be listed in @names,
  * separated by '|'.  Short options are a single hyphen followed by a single
@@ -429,40 +430,38 @@ void opt_usage_exit_fail(const char *msg, ...) NORETURN;
  */
 extern const char opt_hidden[];
 
  */
 extern const char opt_hidden[];
 
-/* Maximum length of arg to show in opt_usage */
-#define OPT_SHOW_LEN 80
-
 /* Standard helpers.  You can write your own: */
 /* Sets the @b to true. */
 char *opt_set_bool(bool *b);
 /* Sets @b based on arg: (yes/no/true/false). */
 char *opt_set_bool_arg(const char *arg, bool *b);
 /* Standard helpers.  You can write your own: */
 /* Sets the @b to true. */
 char *opt_set_bool(bool *b);
 /* Sets @b based on arg: (yes/no/true/false). */
 char *opt_set_bool_arg(const char *arg, bool *b);
-void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b);
+bool opt_show_bool(char *buf, size_t len, const bool *b);
 /* The inverse */
 char *opt_set_invbool(bool *b);
 /* The inverse */
 char *opt_set_invbool(bool *b);
-void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b);
+bool opt_show_invbool(char *buf, size_t len, const bool *b);
 /* Sets @b based on !arg: (yes/no/true/false). */
 char *opt_set_invbool_arg(const char *arg, bool *b);
 
 /* Set a char *. */
 char *opt_set_charp(const char *arg, char **p);
 /* Sets @b based on !arg: (yes/no/true/false). */
 char *opt_set_invbool_arg(const char *arg, bool *b);
 
 /* Set a char *. */
 char *opt_set_charp(const char *arg, char **p);
-void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p);
+/* If *p is NULL, this returns false (i.e. doesn't show a default) */
+bool opt_show_charp(char *buf, size_t len, char *const *p);
 
 /* Set an integer value, various forms.  Sets to 1 on arg == NULL. */
 char *opt_set_intval(const char *arg, int *i);
 
 /* Set an integer value, various forms.  Sets to 1 on arg == NULL. */
 char *opt_set_intval(const char *arg, int *i);
-void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i);
+bool opt_show_intval(char *buf, size_t len, const int *i);
 char *opt_set_uintval(const char *arg, unsigned int *ui);
 char *opt_set_uintval(const char *arg, unsigned int *ui);
-void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui);
+bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui);
 char *opt_set_longval(const char *arg, long *l);
 char *opt_set_longval(const char *arg, long *l);
-void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l);
+bool opt_show_longval(char *buf, size_t len, const long *l);
 char *opt_set_ulongval(const char *arg, unsigned long *ul);
 char *opt_set_ulongval(const char *arg, unsigned long *ul);
-void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul);
+bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul);
 
 /* Set an floating point value, various forms. */
 char *opt_set_floatval(const char *arg, float *f);
 
 /* Set an floating point value, various forms. */
 char *opt_set_floatval(const char *arg, float *f);
-void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f);
+bool opt_show_floatval(char *buf, size_t len, const float *f);
 char *opt_set_doubleval(const char *arg, double *d);
 char *opt_set_doubleval(const char *arg, double *d);
-void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d);
+bool opt_show_doubleval(char *buf, size_t len, const double *d);
 
 /* the following setting functions accept k, M, G, T, P, or E suffixes, which
    multiplies the numeric value by the corresponding power of 1000 or 1024
 
 /* the following setting functions accept k, M, G, T, P, or E suffixes, which
    multiplies the numeric value by the corresponding power of 1000 or 1024
@@ -482,19 +481,19 @@ char *opt_set_ulonglongval_bi(const char *arg, unsigned long long *ll);
 char *opt_set_ulonglongval_si(const char *arg, unsigned long long *ll);
 
 
 char *opt_set_ulonglongval_si(const char *arg, unsigned long long *ll);
 
 
-void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x);
-void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x);
-void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x);
-void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x);
-void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x);
-void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x);
+bool opt_show_intval_bi(char *buf, size_t len, const int *x);
+bool opt_show_longval_bi(char *buf, size_t len, const long *x);
+bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x);
+bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x);
+bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x);
+bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x);
 
 
-void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x);
-void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x);
-void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x);
-void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x);
-void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x);
-void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x);
+bool opt_show_intval_si(char *buf, size_t len, const int *x);
+bool opt_show_longval_si(char *buf, size_t len, const long *x);
+bool opt_show_longlongval_si(char *buf, size_t len, const long long *x);
+bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x);
+bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x);
+bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x);
 
 
 
 
 
 
@@ -509,6 +508,30 @@ char *opt_version_and_exit(const char *version);
 /* Display usage string to stdout, exit(0). */
 char *opt_usage_and_exit(const char *extra);
 
 /* Display usage string to stdout, exit(0). */
 char *opt_usage_and_exit(const char *extra);
 
+/**
+ * opt_find_long: low-level access to the parser
+ * @arg: string of form 'arg' or 'arg=val'.
+ * @optarg: set to `val` of present in arg, otherwise NULL.  Can be NULL.
+ *
+ * Returns NULL if option is unknown.  Sets *@optarg to NULL if
+ * there's no '='.
+ */
+struct opt_table *opt_find_long(const char *arg, const char **optarg);
+
+/**
+ * opt_find_short: low-level access to the parser
+ * @arg: character representing short option
+ *
+ * Returns NULL if option is unknown.
+ */
+struct opt_table *opt_find_short(char arg);
+
+/* opt_type bits reserved for users to play with (ignored!).
+ * You can set bits in type e.g. (1<<OPT_USER_START) to (1<<OPT_USER_END)
+ * when calling _opt_register. */
+#define OPT_USER_START 8
+#define OPT_USER_END 15
+
 /* Below here are private declarations. */
 /* You can use this directly to build tables, but the macros will ensure
  * consistency and type safety. */
 /* Below here are private declarations. */
 /* You can use this directly to build tables, but the macros will ensure
  * consistency and type safety. */
@@ -518,6 +541,11 @@ enum opt_type {
        OPT_SUBTABLE = 4,       /* Actually, longopt points to a subtable... */
        OPT_EARLY = 8,          /* Parse this from opt_early_parse() only. */
        OPT_END = 16,           /* End of the table. */
        OPT_SUBTABLE = 4,       /* Actually, longopt points to a subtable... */
        OPT_EARLY = 8,          /* Parse this from opt_early_parse() only. */
        OPT_END = 16,           /* End of the table. */
+
+       /* Make sure no compiler will assume we never have large
+        * values in the enum! */
+       OPT_USER_MIN = (1 << OPT_USER_START),
+       OPT_USER_MAX = (1 << OPT_USER_END),
 };
 
 struct opt_table {
 };
 
 struct opt_table {
@@ -525,7 +553,7 @@ struct opt_table {
        enum opt_type type;
        char *(*cb)(void *arg); /* OPT_NOARG */
        char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */
        enum opt_type type;
        char *(*cb)(void *arg); /* OPT_NOARG */
        char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */
-       void (*show)(char buf[OPT_SHOW_LEN], const void *arg);
+       bool (*show)(char *buf, size_t len, const void *arg);
        union {
                const void *carg;
                void *arg;
        union {
                const void *carg;
                void *arg;
@@ -551,14 +579,14 @@ struct opt_table {
                          char *(*)(const char *, const typeof(*(arg))*), \
                          char *(*)(const char *, const void *),        \
                          (cb)),                                        \
                          char *(*)(const char *, const typeof(*(arg))*), \
                          char *(*)(const char *, const void *),        \
                          (cb)),                                        \
-       typesafe_cb_cast(void (*)(char buf[], const void *),            \
-                        void (*)(char buf[], const typeof(*(arg))*), (show))
+       typesafe_cb_cast(bool (*)(char *buf, size_t, const void *), \
+                        bool (*)(char *buf, size_t, const typeof(*(arg))*), (show))
 
 /* Non-typesafe register function. */
 void _opt_register(const char *names, enum opt_type type,
                   char *(*cb)(void *arg),
                   char *(*cb_arg)(const char *optarg, void *arg),
 
 /* Non-typesafe register function. */
 void _opt_register(const char *names, enum opt_type type,
                   char *(*cb)(void *arg),
                   char *(*cb_arg)(const char *optarg, void *arg),
-                  void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
+                  bool (*show)(char *buf, size_t len, const void *arg),
                   const void *arg, const char *desc);
 
 /* We use this to get typechecking for OPT_SUBTABLE */
                   const void *arg, const char *desc);
 
 /* We use this to get typechecking for OPT_SUBTABLE */
index d227f7bca2ab679033dd920fe426983dc0ea9314..b932bf333571620d0e19edca1b10a82cc70d8397 100644 (file)
@@ -14,7 +14,8 @@
 /tmp/opt-example: option requires an argument -- 's'
 */
 static int parse_err(void (*errlog)(const char *fmt, ...),
 /tmp/opt-example: option requires an argument -- 's'
 */
 static int parse_err(void (*errlog)(const char *fmt, ...),
-                    const char *argv0, const char *arg, unsigned len,
+                    const char *argv0,
+                    const char *arg, unsigned len,
                     const char *problem)
 {
        errlog("%s: %.*s: %s", argv0, len, arg, problem);
                     const char *problem)
 {
        errlog("%s: %.*s: %s", argv0, len, arg, problem);
@@ -28,13 +29,63 @@ static void consume_option(int *argc, char *argv[], unsigned optnum)
        (*argc)--;
 }
 
        (*argc)--;
 }
 
+/* This sets the len and o to indicate how far it is into the
+ * opt_table's names field. */
+static struct opt_table *opt_find_long_extra(const char *arg,
+                                            const char **optarg,
+                                            unsigned int *len,
+                                            const char **o)
+{
+       unsigned i;
+
+       *optarg = NULL;
+       for (*o = first_lopt(&i, len);
+            *o;
+            *o = next_lopt(*o, &i, len)) {
+               if (strncmp(arg, *o, *len) != 0)
+                       continue;
+               if (arg[*len] == '=')
+                       *optarg = arg + *len + 1;
+               else if (arg[*len] != '\0')
+                       continue;
+               return &opt_table[i];
+
+       }
+       return NULL;
+}
+
+struct opt_table *opt_find_long(const char *arg, const char **optarg)
+{
+       unsigned len;
+       const char *o;
+
+       return opt_find_long_extra(arg, optarg ? optarg : &o, &len, &o);
+}
+
+static struct opt_table *opt_find_short_extra(char arg, const char **o)
+{
+       unsigned i;
+       for (*o = first_sopt(&i); *o; *o = next_sopt(*o, &i)) {
+               if (arg == **o)
+                       return &opt_table[i];
+       }
+       return NULL;
+}
+
+struct opt_table *opt_find_short(char arg)
+{
+       const char *o;
+       return opt_find_short_extra(arg, &o);
+}
+
 /* Returns 1 if argument consumed, 0 if all done, -1 on error. */
 int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
              void (*errlog)(const char *fmt, ...), bool unknown_ok)
 {
 /* Returns 1 if argument consumed, 0 if all done, -1 on error. */
 int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
              void (*errlog)(const char *fmt, ...), bool unknown_ok)
 {
-       unsigned i, arg, len;
+       unsigned arg, len;
        const char *o, *optarg = NULL;
        char *problem = NULL;
        const char *o, *optarg = NULL;
        char *problem = NULL;
+       struct opt_table *ot;
 
        if (getenv("POSIXLY_CORRECT")) {
                /* Don't find options after non-options. */
 
        if (getenv("POSIXLY_CORRECT")) {
                /* Don't find options after non-options. */
@@ -58,34 +109,22 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
        /* Long options start with -- */
        if (argv[arg][1] == '-') {
                assert(*offset == 0);
        /* Long options start with -- */
        if (argv[arg][1] == '-') {
                assert(*offset == 0);
-               for (o = first_lopt(&i, &len); o; o = next_lopt(o, &i, &len)) {
-                       if (strncmp(argv[arg] + 2, o, len) != 0)
-                               continue;
-                       if (argv[arg][2 + len] == '=')
-                               optarg = argv[arg] + 2 + len + 1;
-                       else if (argv[arg][2 + len] != '\0')
-                               continue;
-                       break;
-               }
-               if (!o) {
+
+               ot = opt_find_long_extra(argv[arg]+2, &optarg, &len, &o);
+               if (!ot) {
                        if (unknown_ok)
                                goto ok;
                        return parse_err(errlog, argv[0],
                                         argv[arg], strlen(argv[arg]),
                                         "unrecognized option");
                }
                        if (unknown_ok)
                                goto ok;
                        return parse_err(errlog, argv[0],
                                         argv[arg], strlen(argv[arg]),
                                         "unrecognized option");
                }
+
                /* For error messages, we include the leading '--' */
                o -= 2;
                len += 2;
        } else {
                /* For error messages, we include the leading '--' */
                o -= 2;
                len += 2;
        } else {
-               /* offset allows us to handle -abc */
-               for (o = first_sopt(&i); o; o = next_sopt(o, &i)) {
-                       if (argv[arg][*offset + 1] != *o)
-                               continue;
-                       (*offset)++;
-                       break;
-               }
-               if (!o) {
+               ot = opt_find_short_extra(argv[arg][*offset + 1], &o);
+               if (!ot) {
                        if (unknown_ok) {
                                (*offset)++;
                                goto ok;
                        if (unknown_ok) {
                                (*offset)++;
                                goto ok;
@@ -94,17 +133,19 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
                                         argv[arg], strlen(argv[arg]),
                                         "unrecognized option");
                }
                                         argv[arg], strlen(argv[arg]),
                                         "unrecognized option");
                }
+
+               (*offset)++;
                /* For error messages, we include the leading '-' */
                o--;
                len = 2;
        }
 
                /* For error messages, we include the leading '-' */
                o--;
                len = 2;
        }
 
-       if ((opt_table[i].type & ~OPT_EARLY) == OPT_NOARG) {
+       if (ot->type & OPT_NOARG) {
                if (optarg)
                        return parse_err(errlog, argv[0], o, len,
                                         "doesn't allow an argument");
                if (optarg)
                        return parse_err(errlog, argv[0], o, len,
                                         "doesn't allow an argument");
-               if ((opt_table[i].type & OPT_EARLY) == is_early)
-                       problem = opt_table[i].cb(opt_table[i].u.arg);
+               if ((ot->type & OPT_EARLY) == is_early)
+                       problem = ot->cb(ot->u.arg);
        } else {
                if (!optarg) {
                        /* Swallow any short options as optarg, eg -afile */
        } else {
                if (!optarg) {
                        /* Swallow any short options as optarg, eg -afile */
@@ -117,9 +158,8 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
                if (!optarg)
                        return parse_err(errlog, argv[0], o, len,
                                         "requires an argument");
                if (!optarg)
                        return parse_err(errlog, argv[0], o, len,
                                         "requires an argument");
-               if ((opt_table[i].type & OPT_EARLY) == is_early)
-                       problem = opt_table[i].cb_arg(optarg,
-                                                     opt_table[i].u.arg);
+               if ((ot->type & OPT_EARLY) == is_early)
+                       problem = ot->cb_arg(optarg, ot->u.arg);
        }
 
        if (problem) {
        }
 
        if (problem) {
index b559c7f747689bae70d34589ee9794f4cf710ac0..03e6986dd191954c7f14491353db1adfe0e51d51 100644 (file)
@@ -4,15 +4,24 @@
 #include <ccan/opt/helpers.c>
 #include <ccan/opt/parse.c>
 
 #include <ccan/opt/helpers.c>
 #include <ccan/opt/parse.c>
 
-static void show_10(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED)
+static bool show_10(char *buf, size_t len, const void *arg UNNEEDED)
 {
        memset(buf, 'X', 10);
        buf[10] = '\0';
 {
        memset(buf, 'X', 10);
        buf[10] = '\0';
+       return true;
 }
 
 }
 
-static void show_max(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED)
+static bool show_10_false(char *buf, size_t len, const void *arg UNNEEDED)
+{
+       memset(buf, 'X', 10);
+       buf[10] = '\0';
+       return false;
+}
+
+static bool show_max(char *buf, size_t len, const void *arg UNNEEDED)
 {
        memset(buf, 'X', OPT_SHOW_LEN);
 {
        memset(buf, 'X', OPT_SHOW_LEN);
+       return true;
 }
 
 /* Test add_desc helper. */
 }
 
 /* Test add_desc helper. */
@@ -22,7 +31,7 @@ int main(void)
        char *ret;
        size_t len, max;
 
        char *ret;
        size_t len, max;
 
-       plan_tests(30);
+       plan_tests(32);
 
        opt.show = NULL;
        opt.names = "01234";
 
        opt.show = NULL;
        opt.names = "01234";
@@ -113,6 +122,14 @@ int main(void)
                   "        (default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...)\n") == 0);
        free(ret); len = max = 0;
 
                   "        (default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...)\n") == 0);
        free(ret); len = max = 0;
 
+       /* With show function which fails doesn't print. */
+       opt.show = show_10_false;
+       ret = add_desc(NULL, &len, &max, 7, 41, &opt);
+       ok1(len < max);
+       ret[len] = '\0';
+       ok1(strcmp(ret, "01234  0123456789 0\n") == 0);
+       free(ret); len = max = 0;
+
        /* With added " <arg>".  Fits, just. */
        opt.show = NULL;
        opt.type = OPT_HASARG;
        /* With added " <arg>".  Fits, just. */
        opt.show = NULL;
        opt.type = OPT_HASARG;
index 8534f291ac3e9accfeafb0685fc61286b6adf4b6..0c4f6c8693972b489440feddd1a38b260a8011f9 100644 (file)
@@ -10,7 +10,7 @@
 
 int main(int argc, char *argv[])
 {
 
 int main(int argc, char *argv[])
 {
-       plan_tests(12);
+       plan_tests(14);
 
        /* --aaa without args. */
        opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "");
 
        /* --aaa without args. */
        opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "");
@@ -42,6 +42,10 @@ int main(int argc, char *argv[])
        free(err_output);
        err_output = NULL;
 
        free(err_output);
        err_output = NULL;
 
+       opt_register_noarg("-d", test_noarg, NULL, "");
+       ok1(!parse_args(&argc, &argv, "-dc", NULL));
+       ok1(strstr(err_output, ": -c: requires an argument"));
+
        /* parse_args allocates argv */
        free(argv);
        return exit_status();
        /* parse_args allocates argv */
        free(argv);
        return exit_status();
index 0a08a85f7aa3930011cba5600186f3a21d5c5f09..9aa41fe8db62c5f384b517a811b9b49b3ca3577d 100644 (file)
@@ -476,26 +476,26 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
-                       opt_show_intval_bi(buf, &i);
+                       opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-77") == 0);
                        i = 0;
                        ok1(strcmp(buf, "-77") == 0);
                        i = 0;
-                       opt_show_intval_bi(buf, &i);
+                       opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
-                       opt_show_intval_bi(buf, &i);
+                       opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1234 * k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1234 * k;
-                       opt_show_intval_bi(buf, &i);
+                       opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "-1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_intval_bi(buf, &i);
+                       opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
-                       opt_show_intval_bi(buf, &i);
+                       opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -506,27 +506,27 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
-                       opt_show_longval_bi(buf, &i);
+                       opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
                        ok1(strcmp(buf, "-77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
-                       opt_show_longval_bi(buf, &i);
+                       opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1 * k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1 * k;
-                       opt_show_longval_bi(buf, &i);
+                       opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "-1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_longval_bi(buf, &i);
+                       opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
-                       opt_show_longval_bi(buf, &i);
+                       opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
-                       opt_show_longval_bi(buf, &i);
+                       opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -537,23 +537,23 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -7777;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -7777;
-                       opt_show_longlongval_bi(buf, &i);
+                       opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 7777;
                        ok1(strcmp(buf, "-7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 7777;
-                       opt_show_longlongval_bi(buf, &i);
+                       opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -10240000 * k;
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -10240000 * k;
-                       opt_show_longlongval_bi(buf, &i);
+                       opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-10000M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
                        ok1(strcmp(buf, "-10000M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
-                       opt_show_longlongval_bi(buf, &i);
+                       opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * P;
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * P;
-                       opt_show_longlongval_bi(buf, &i);
+                       opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "1E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -564,19 +564,19 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
-                       opt_show_uintval_bi(buf, &i);
+                       opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1234 * k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1234 * k;
-                       opt_show_uintval_bi(buf, &i);
+                       opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_uintval_bi(buf, &i);
+                       opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
-                       opt_show_uintval_bi(buf, &i);
+                       opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -587,23 +587,23 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
-                       opt_show_ulongval_bi(buf, &i);
+                       opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = k;
-                       opt_show_ulongval_bi(buf, &i);
+                       opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_ulongval_bi(buf, &i);
+                       opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
-                       opt_show_ulongval_bi(buf, &i);
+                       opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
-                       opt_show_ulongval_bi(buf, &i);
+                       opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -614,19 +614,19 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 7777;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 7777;
-                       opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 10240000 * k;
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 10240000 * k;
-                       opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "10000M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
                        ok1(strcmp(buf, "10000M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
-                       opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * P;
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * P;
-                       opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "1E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "1E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -860,26 +860,26 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
-                       opt_show_intval_si(buf, &i);
+                       opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-77") == 0);
                        i = 0;
                        ok1(strcmp(buf, "-77") == 0);
                        i = 0;
-                       opt_show_intval_si(buf, &i);
+                       opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
-                       opt_show_intval_si(buf, &i);
+                       opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1234 * k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1234 * k;
-                       opt_show_intval_si(buf, &i);
+                       opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "-1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_intval_si(buf, &i);
+                       opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * M;
-                       opt_show_intval_si(buf, &i);
+                       opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -890,27 +890,27 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
-                       opt_show_longval_si(buf, &i);
+                       opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
                        ok1(strcmp(buf, "-77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 77;
-                       opt_show_longval_si(buf, &i);
+                       opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1 * k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -1 * k;
-                       opt_show_longval_si(buf, &i);
+                       opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "-1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_longval_si(buf, &i);
+                       opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * M;
-                       opt_show_longval_si(buf, &i);
+                       opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
-                       opt_show_longval_si(buf, &i);
+                       opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -921,23 +921,23 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -7777;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -7777;
-                       opt_show_longlongval_si(buf, &i);
+                       opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 7777;
                        ok1(strcmp(buf, "-7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 7777;
-                       opt_show_longlongval_si(buf, &i);
+                       opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -10240000 * k;
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = -10240000 * k;
-                       opt_show_longlongval_si(buf, &i);
+                       opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "-10240M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
                        ok1(strcmp(buf, "-10240M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
-                       opt_show_longlongval_si(buf, &i);
+                       opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 2000 * P;
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 2000 * P;
-                       opt_show_longlongval_si(buf, &i);
+                       opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "2E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "2E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -948,19 +948,19 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
-                       opt_show_uintval_si(buf, &i);
+                       opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1234 * k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1234 * k;
-                       opt_show_uintval_si(buf, &i);
+                       opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "1234k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_uintval_si(buf, &i);
+                       opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * M;
-                       opt_show_uintval_si(buf, &i);
+                       opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "1G") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -971,23 +971,23 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 77;
-                       opt_show_ulongval_si(buf, &i);
+                       opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = k;
                        ok1(strcmp(buf, "77") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = k;
-                       opt_show_ulongval_si(buf, &i);
+                       opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
                        ok1(strcmp(buf, "1k") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 500 * M;
-                       opt_show_ulongval_si(buf, &i);
+                       opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
                        ok1(strcmp(buf, "500M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1024 * M;
-                       opt_show_ulongval_si(buf, &i);
+                       opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "1024M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
                        ok1(strcmp(buf, "1024M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 0;
-                       opt_show_ulongval_si(buf, &i);
+                       opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "0") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -998,19 +998,19 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 7777;
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = 7777;
-                       opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 10240000 * k;
                        ok1(strcmp(buf, "7777") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 10240000 * k;
-                       opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "10240M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
                        ok1(strcmp(buf, "10240M") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 5 * P;
-                       opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * P;
                        ok1(strcmp(buf, "5P") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                        i = 1000 * P;
-                       opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+                       opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
                        ok1(strcmp(buf, "1E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
                        ok1(strcmp(buf, "1E") == 0);
                        ok1(buf[OPT_SHOW_LEN] == '!');
                }
@@ -1090,12 +1090,12 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                b = true;
                buf[OPT_SHOW_LEN] = '!';
 
                b = true;
-               opt_show_bool(buf, &b);
+               opt_show_bool(buf, OPT_SHOW_LEN, &b);
                ok1(strcmp(buf, "true") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                b = false;
                ok1(strcmp(buf, "true") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                b = false;
-               opt_show_bool(buf, &b);
+               opt_show_bool(buf, OPT_SHOW_LEN, &b);
                ok1(strcmp(buf, "false") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "false") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
@@ -1107,12 +1107,12 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                b = true;
                buf[OPT_SHOW_LEN] = '!';
 
                b = true;
-               opt_show_invbool(buf, &b);
+               opt_show_invbool(buf, OPT_SHOW_LEN, &b);
                ok1(strcmp(buf, "false") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                b = false;
                ok1(strcmp(buf, "false") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                b = false;
-               opt_show_invbool(buf, &b);
+               opt_show_invbool(buf, OPT_SHOW_LEN, &b);
                ok1(strcmp(buf, "true") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "true") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
@@ -1126,14 +1126,14 @@ int main(int argc, char *argv[])
                /* Short test. */
                p = str;
                strcpy(p, "short");
                /* Short test. */
                p = str;
                strcpy(p, "short");
-               opt_show_charp(buf, &p);
+               opt_show_charp(buf, OPT_SHOW_LEN, &p);
                ok1(strcmp(buf, "\"short\"") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                /* Truncate test. */
                memset(p, 'x', OPT_SHOW_LEN*2);
                p[OPT_SHOW_LEN*2-1] = '\0';
                ok1(strcmp(buf, "\"short\"") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                /* Truncate test. */
                memset(p, 'x', OPT_SHOW_LEN*2);
                p[OPT_SHOW_LEN*2-1] = '\0';
-               opt_show_charp(buf, &p);
+               opt_show_charp(buf, OPT_SHOW_LEN, &p);
                ok1(buf[0] == '"');
                ok1(buf[OPT_SHOW_LEN-1] == '"');
                ok1(buf[OPT_SHOW_LEN] == '!');
                ok1(buf[0] == '"');
                ok1(buf[OPT_SHOW_LEN-1] == '"');
                ok1(buf[OPT_SHOW_LEN] == '!');
@@ -1147,12 +1147,12 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                i = -77;
                buf[OPT_SHOW_LEN] = '!';
 
                i = -77;
-               opt_show_intval(buf, &i);
+               opt_show_intval(buf, OPT_SHOW_LEN, &i);
                ok1(strcmp(buf, "-77") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                i = 77;
                ok1(strcmp(buf, "-77") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                i = 77;
-               opt_show_intval(buf, &i);
+               opt_show_intval(buf, OPT_SHOW_LEN, &i);
                ok1(strcmp(buf, "77") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "77") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
@@ -1164,7 +1164,7 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                ui = 4294967295U;
                buf[OPT_SHOW_LEN] = '!';
 
                ui = 4294967295U;
-               opt_show_uintval(buf, &ui);
+               opt_show_uintval(buf, OPT_SHOW_LEN, &ui);
                ok1(strcmp(buf, "4294967295") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "4294967295") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
@@ -1176,7 +1176,7 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                l = 1234567890L;
                buf[OPT_SHOW_LEN] = '!';
 
                l = 1234567890L;
-               opt_show_longval(buf, &l);
+               opt_show_longval(buf, OPT_SHOW_LEN, &l);
                ok1(strcmp(buf, "1234567890") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "1234567890") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
@@ -1188,7 +1188,7 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                ul = 4294967295UL;
                buf[OPT_SHOW_LEN] = '!';
 
                ul = 4294967295UL;
-               opt_show_ulongval(buf, &ul);
+               opt_show_ulongval(buf, OPT_SHOW_LEN, &ul);
                ok1(strcmp(buf, "4294967295") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "4294967295") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
@@ -1200,12 +1200,12 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                f = -77.5;
                buf[OPT_SHOW_LEN] = '!';
 
                f = -77.5;
-               opt_show_floatval(buf, &f);
+               opt_show_floatval(buf, OPT_SHOW_LEN, &f);
                ok1(strcmp(buf, "-77.500000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                f = 77.5;
                ok1(strcmp(buf, "-77.500000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                f = 77.5;
-               opt_show_floatval(buf, &f);
+               opt_show_floatval(buf, OPT_SHOW_LEN, &f);
                ok1(strcmp(buf, "77.500000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "77.500000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
@@ -1217,12 +1217,12 @@ int main(int argc, char *argv[])
                buf[OPT_SHOW_LEN] = '!';
 
                d = -77;
                buf[OPT_SHOW_LEN] = '!';
 
                d = -77;
-               opt_show_doubleval(buf, &d);
+               opt_show_doubleval(buf, OPT_SHOW_LEN, &d);
                ok1(strcmp(buf, "-77.000000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                d = 77;
                ok1(strcmp(buf, "-77.000000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
 
                d = 77;
-               opt_show_doubleval(buf, &d);
+               opt_show_doubleval(buf, OPT_SHOW_LEN, &d);
                ok1(strcmp(buf, "77.000000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
                ok1(strcmp(buf, "77.000000") == 0);
                ok1(buf[OPT_SHOW_LEN] == '!');
        }
index 1dbb351bedf41b0022a8abd26b3c0fbbc07ad445..2d7410ae22853e6bb9fbe6c3896c58dc01773a5c 100644 (file)
@@ -59,8 +59,8 @@ static void *reallocfn(void *ptr, size_t size)
 static void freefn(void *ptr)
 {
        free_count++;
 static void freefn(void *ptr)
 {
        free_count++;
-       free(ptr);
        *find_ptr(ptr) = NULL;
        *find_ptr(ptr) = NULL;
+       free(ptr);
 }
 
 int main(int argc, char *argv[])
 }
 
 int main(int argc, char *argv[])
diff --git a/ccan/opt/test/run-userbits.c b/ccan/opt/test/run-userbits.c
new file mode 100644 (file)
index 0000000..7f102f0
--- /dev/null
@@ -0,0 +1,59 @@
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+int main(int argc, char *argv[])
+{
+       const char *myname = argv[0];
+
+       plan_tests(28);
+
+       opt_register_noarg("-a", test_noarg, NULL, "All");
+       opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll");
+       opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "AAAAAAll");
+
+       ok1(strcmp(opt_table[0].names, "-a") == 0);
+       ok1(opt_table[0].type == OPT_NOARG);
+       ok1(strcmp(opt_table[1].names, "--aaa") == 0);
+       ok1(opt_table[1].type == OPT_NOARG);
+       ok1(strcmp(opt_table[2].names, "-b|--bbb") == 0);
+       ok1(opt_table[2].type == OPT_HASARG);
+
+       opt_table[0].type |= (1 << OPT_USER_START);
+       opt_table[1].type |= ((1 << OPT_USER_END)-1) - ((1 << OPT_USER_START)-1);
+       opt_table[2].type |= (1 << OPT_USER_END);
+
+       /* Should all work fine! */
+       ok1(parse_args(&argc, &argv, "-a", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(test_cb_called == 1);
+
+       ok1(parse_args(&argc, &argv, "--aaa", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(test_cb_called == 2);
+
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "-b", NULL) == false);
+       ok1(test_cb_called == 2);
+       ok1(parse_args(&argc, &argv, "-b", "bbb", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 3);
+
+       ok1(parse_args(&argc, &argv, "--bbb", "bbb", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 4);
+
+       /* parse_args allocates argv */
+       free(argv);
+       return exit_status();
+}
index 2ff04884ebdc0efe6dc4d9dd8fe4ae6af4b9175b..61199fb4676dbdeaa8a5cdf81a6fba7a27d5de5d 100644 (file)
@@ -21,9 +21,10 @@ char *test_arg(const char *optarg, const char *arg)
        return NULL;
 }
 
        return NULL;
 }
 
-void show_arg(char buf[OPT_SHOW_LEN], const char *arg)
+bool show_arg(char *buf, size_t len, const char *arg)
 {
 {
-       strncpy(buf, arg, OPT_SHOW_LEN);
+       strncpy(buf, arg, len);
+       return true;
 }
 
 char *err_output = NULL;
 }
 
 char *err_output = NULL;
index 12cf0b753e99be441ead57f73b1dc1c3e2d79999..3ada62d117c048ef666f5078d874c5bcc6a47129 100644 (file)
@@ -13,7 +13,7 @@ void reset_options(void);
 extern unsigned int test_cb_called;
 char *test_noarg(void *arg);
 char *test_arg(const char *optarg, const char *arg);
 extern unsigned int test_cb_called;
 char *test_noarg(void *arg);
 char *test_arg(const char *optarg, const char *arg);
-void show_arg(char buf[OPT_SHOW_LEN], const char *arg);
+bool show_arg(char *buf, size_t len, const char *arg);
 
 extern struct opt_table short_table[];
 extern struct opt_table long_table[];
 
 extern struct opt_table short_table[];
 extern struct opt_table long_table[];
index 12f44a48752e0e0d4e0d722c74b7513561334733..568e4661d8098bf1fd009a169867b0d369339fb2 100644 (file)
@@ -20,6 +20,9 @@ const char opt_hidden[1];
 #define MIN_DESC_WIDTH 40
 #define MIN_TOTAL_WIDTH 50
 
 #define MIN_DESC_WIDTH 40
 #define MIN_TOTAL_WIDTH 50
 
+/* Maximum length of arg to show in opt_usage */
+#define OPT_SHOW_LEN 80
+
 static unsigned int get_columns(void)
 {
        int ws_col = 0;
 static unsigned int get_columns(void)
 {
        int ws_col = 0;
@@ -72,7 +75,8 @@ static size_t consume_words(const char *words, size_t maxlen, size_t *prefix,
                }
        }
 
                }
        }
 
-       *start = (words[oldlen - 1] == '\n');
+       if (oldlen != 0)
+               *start = (words[oldlen - 1] == '\n');
        return oldlen;
 }
 
        return oldlen;
 }
 
@@ -147,20 +151,20 @@ static char *add_desc(char *base, size_t *len, size_t *max,
        if (opt->show) {
                char buf[OPT_SHOW_LEN + sizeof("...")];
                strcpy(buf + OPT_SHOW_LEN, "...");
        if (opt->show) {
                char buf[OPT_SHOW_LEN + sizeof("...")];
                strcpy(buf + OPT_SHOW_LEN, "...");
-               opt->show(buf, opt->u.arg);
+               if (opt->show(buf, OPT_SHOW_LEN, opt->u.arg)) {
+                       /* If it doesn't fit on this line, indent. */
+                       if (off + strlen(" (default: ") + strlen(buf) + strlen(")")
+                           > width) {
+                               base = add_indent(base, len, max, indent);
+                       } else {
+                               /* Remove \n. */
+                               (*len)--;
+                       }
 
 
-               /* If it doesn't fit on this line, indent. */
-               if (off + strlen(" (default: ") + strlen(buf) + strlen(")")
-                   > width) {
-                       base = add_indent(base, len, max, indent);
-               } else {
-                       /* Remove \n. */
-                       (*len)--;
+                       base = add_str(base, len, max, " (default: ");
+                       base = add_str(base, len, max, buf);
+                       base = add_str(base, len, max, ")\n");
                }
                }
-
-               base = add_str(base, len, max, " (default: ");
-               base = add_str(base, len, max, buf);
-               base = add_str(base, len, max, ")\n");
        }
        return base;
 }
        }
        return base;
 }
@@ -181,10 +185,10 @@ char *opt_usage(const char *argv0, const char *extra)
                size_t l;
                if (opt_table[i].desc == opt_hidden)
                        continue;
                size_t l;
                if (opt_table[i].desc == opt_hidden)
                        continue;
-               if (opt_table[i].type == OPT_SUBTABLE)
+               if (opt_table[i].type & OPT_SUBTABLE)
                        continue;
                l = strlen(opt_table[i].names);
                        continue;
                l = strlen(opt_table[i].names);
-               if (opt_table[i].type == OPT_HASARG
+               if ((opt_table[i].type & OPT_HASARG)
                    && !strchr(opt_table[i].names, ' ')
                    && !strchr(opt_table[i].names, '='))
                        l += strlen(" <arg>");
                    && !strchr(opt_table[i].names, ' ')
                    && !strchr(opt_table[i].names, '='))
                        l += strlen(" <arg>");
@@ -220,7 +224,7 @@ char *opt_usage(const char *argv0, const char *extra)
        for (i = 0; i < opt_count; i++) {
                if (opt_table[i].desc == opt_hidden)
                        continue;
        for (i = 0; i < opt_count; i++) {
                if (opt_table[i].desc == opt_hidden)
                        continue;
-               if (opt_table[i].type == OPT_SUBTABLE) {
+               if (opt_table[i].type & OPT_SUBTABLE) {
                        ret = add_str(ret, &len, &max, opt_table[i].desc);
                        ret = add_str(ret, &len, &max, ":\n");
                        continue;
                        ret = add_str(ret, &len, &max, opt_table[i].desc);
                        ret = add_str(ret, &len, &max, ":\n");
                        continue;
index 8c49511a250c2cbc99f93d4628d80dd0c5c971f2..824f22e3ddf003ea68884943912044a707717f76 100644 (file)
@@ -51,6 +51,7 @@ int main(int argc, char *argv[])
                return 1;
 
        if (strcmp(argv[1], "depends") == 0) {
                return 1;
 
        if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/closefrom\n");
                printf("ccan/noerr\n");
                return 0;
        }
                printf("ccan/noerr\n");
                return 0;
        }
index afeaf5a0a256de9839b39b522f0cc1e3e8f142a9..0090275b01554a6654b810c35518a21c59c2a0b5 100644 (file)
@@ -1,4 +1,5 @@
 /* CC0 license (public domain) - see LICENSE file for details */
 /* CC0 license (public domain) - see LICENSE file for details */
+#include <ccan/closefrom/closefrom.h>
 #include <ccan/pipecmd/pipecmd.h>
 #include <ccan/noerr/noerr.h>
 #include <stdlib.h>
 #include <ccan/pipecmd/pipecmd.h>
 #include <ccan/noerr/noerr.h>
 #include <stdlib.h>
@@ -115,7 +116,8 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
                goto fail;
 
        if (childpid == 0) {
                goto fail;
 
        if (childpid == 0) {
-               for (int i = 0; i < num_child_close; i++)
+               int i;
+               for (i = 0; i < num_child_close; i++)
                        close(child_close[i]);
 
                // Child runs command.
                        close(child_close[i]);
 
                // Child runs command.
@@ -138,11 +140,21 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
                        close(errfromchild[1]);
                }
 
                        close(errfromchild[1]);
                }
 
+               /* Map execfail[1] to fd 3.  */
+               if (execfail[1] != 3) {
+                       if (dup2(execfail[1], 3) == -1)
+                               goto child_errno_fail;
+                       /* CLOEXEC is not shared by dup2, so copy the flags
+                        * from execfail[1] to 3.
+                        */
+                       if (fcntl(3, F_SETFD, fcntl(execfail[1], F_GETFD)) < 0)
+                               goto child_errno_fail;
+                       close(execfail[1]);
+                       execfail[1] = 3;
+               }
+
                /* Make (fairly!) sure all other fds are closed. */
                /* Make (fairly!) sure all other fds are closed. */
-               int max = sysconf(_SC_OPEN_MAX);
-               for (int i = 3; i < max; i++)
-                       if (i != execfail[1])
-                               close(i);
+               closefrom(4);
 
                execvp(arr[0], arr);
 
 
                execvp(arr[0], arr);
 
@@ -155,7 +167,8 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
                exit(127);
        }
 
                exit(127);
        }
 
-       for (int i = 0; i < num_par_close; i++)
+       int i;
+       for (i = 0; i < num_par_close; i++)
                close(par_close[i]);
 
        /* Child will close this without writing on successful exec. */
                close(par_close[i]);
 
        /* Child will close this without writing on successful exec. */
@@ -175,7 +188,7 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
        return childpid;
 
 fail:
        return childpid;
 
 fail:
-       for (int i = 0; i < num_par_close; i++)
+       for (i = 0; i < num_par_close; i++)
                close_noerr(par_close[i]);
        return -1;
 }
                close_noerr(par_close[i]);
        return -1;
 }
index d8d658d37a3923454324dfe06a3d4bac777b91d0..cc10cf3d7f25e25531d58a9c4b07c8500ff93bf7 100644 (file)
@@ -74,9 +74,11 @@ char *rbuf_read_str(struct rbuf *rbuf, char term)
        ssize_t r = 0;
        size_t prev = 0;
 
        ssize_t r = 0;
        size_t prev = 0;
 
-       while (!(p = memchr(membuf_elems(&rbuf->m) + prev,
-                           term,
-                           membuf_num_elems(&rbuf->m) - prev))) {
+       /* memchr(NULL, ..., 0) is illegal.  FML. */
+       while (membuf_num_elems(&rbuf->m) == prev
+              || !(p = memchr(membuf_elems(&rbuf->m) + prev,
+                              term,
+                              membuf_num_elems(&rbuf->m) - prev))) {
                prev += r;
                r = get_more(rbuf);
                if (r < 0)
                prev += r;
                r = get_more(rbuf);
                if (r < 0)
diff --git a/ccan/rune/LICENSE b/ccan/rune/LICENSE
new file mode 120000 (symlink)
index 0000000..2354d12
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/rune/_info b/ccan/rune/_info
new file mode 100644 (file)
index 0000000..2b2e2e8
--- /dev/null
@@ -0,0 +1,130 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * rune - Simple cookies you can extend (a-la Python runes class).
+ *
+ * This code is a form of cookies, but they are user-extensible, and
+ * contain a simple language to define what the cookie allows.
+ *
+ * A "rune" contains the hash of a secret (so the server can
+ * validate), such that you can add, but not subtract, conditions.
+ * This is a simplified form of Macaroons, See
+ * https://research.google/pubs/pub41892/ "Macaroons: Cookies with
+ * Contextual Caveats for Decentralized Authorization in the Cloud".
+ * It has one good idea, some extended ideas nobody implements, and
+ * lots and lots of words.
+ *
+ * License: BSD-MIT
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ * Example:
+ * // Given "generate secret 1" outputs kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ==
+ * // Given "add kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ== uid=rusty" outputs Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk=
+ * // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= rusty" outputs PASSED
+ * // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= notrusty" outputs FAILED: uid is not equal to rusty
+ * // Given "add Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= t\<1655958616" outputs _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2
+ * // Given "test secret _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2 rusty" outputs FAILED: t is greater or equal to 1655958616
+ * #include <ccan/err/err.h>
+ * #include <ccan/rune/rune.h>
+ * #include <ccan/str/str.h>
+ * #include <stdio.h>
+ * #include <sys/time.h>
+ * 
+ * // We support two values: current time (t), and user id (uid).
+ * static const char *check(const tal_t *ctx,
+ *                         const struct rune *rune,
+ *                         const struct rune_altern *alt,
+ *                         char *uid)
+ * {
+ *     // t= means current time, in seconds, as integer
+ *     if (streq(alt->fieldname, "t")) {
+ *             struct timeval now;
+ *             gettimeofday(&now, NULL);
+ *             return rune_alt_single_int(ctx, alt, now.tv_sec);
+ *     }
+ *     if (streq(alt->fieldname, "uid")) {
+ *             return rune_alt_single_str(ctx, alt, uid, strlen(uid));
+ *     }
+ *     // Otherwise, field is missing
+ *     return rune_alt_single_missing(ctx, alt);
+ * }
+ * 
+ * int main(int argc, char *argv[])
+ * {
+ *     struct rune *master, *rune;
+ * 
+ *     if (argc < 3)
+ *             goto usage;
+ * 
+ *     if (streq(argv[1], "generate")) {
+ *             // Make master, derive a unique_id'd rune.
+ *             if (argc != 3 && argc != 4)
+ *                     goto usage;
+ *             master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL);
+ *             rune = rune_derive_start(NULL, master, argv[3]);
+ *     } else if (streq(argv[1], "add")) {
+ *             // Add a restriction
+ *             struct rune_restr *restr;
+ *             if (argc != 4)
+ *                     goto usage;
+ *             rune = rune_from_base64(NULL, argv[2]);
+ *             if (!rune)
+ *                     errx(1, "Bad rune");
+ *             restr = rune_restr_from_string(NULL, argv[3], strlen(argv[3]));
+ *             if (!restr)
+ *                     errx(1, "Bad restriction string");
+ *             rune_add_restr(rune, restr);
+ *     } else if (streq(argv[1], "test")) {
+ *             const char *err;
+ *             if (argc != 5)
+ *                     goto usage;
+ *             master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL);
+ *             if (!master)
+ *                     errx(1, "Bad master rune");
+ *             rune = rune_from_base64(NULL, argv[3]);
+ *             if (!rune)
+ *                     errx(1, "Bad rune");
+ *             err = rune_test(NULL, master, rune, check, argv[4]);
+ *             if (err)
+ *                     printf("FAILED: %s\n", err);
+ *             else
+ *                     printf("PASSED\n");
+ *             return 0;
+ *     } else
+ *             goto usage;
+ * 
+ *     printf("%s\n", rune_to_base64(NULL, rune));
+ *     return 0;
+ * 
+ * usage:
+ *     errx(1, "Usage: %s generate <secret> <uniqueid> OR\n"
+ *          "%s add <rune> <restriction> OR\n"
+ *          "%s test <secret> <rune> <uid>", argv[0], argv[0], argv[0]);
+ * }
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/base64\n");
+               printf("ccan/crypto/sha256\n");
+               printf("ccan/endian\n");
+               printf("ccan/mem\n");
+               printf("ccan/short_types\n");
+               printf("ccan/str/hex\n");
+               printf("ccan/tal/str\n");
+               printf("ccan/tal\n");
+               printf("ccan/typesafe_cb\n");
+               return 0;
+       }
+       if (strcmp(argv[1], "testdepends") == 0) {
+               printf("ccan/tal/grab_file\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/rune/coding.c b/ccan/rune/coding.c
new file mode 100644 (file)
index 0000000..495d37c
--- /dev/null
@@ -0,0 +1,426 @@
+/* MIT (BSD) license - see LICENSE file for details */
+/* Routines to encode / decode a rune */
+#include <ccan/rune/rune.h>
+#include <ccan/rune/internal.h>
+#include <ccan/str/hex/hex.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/base64/base64.h>
+#include <ccan/endian/endian.h>
+#include <errno.h>
+
+/* From Python base64.urlsafe_b64encode:
+ *
+ * The alphabet uses '-' instead of '+' and '_' instead of '/'.
+ */
+static const base64_maps_t base64_maps_urlsafe = {
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
+
+  "\xff\xff\xff\xff\xff" /* 0 */
+  "\xff\xff\xff\xff\xff" /* 5 */
+  "\xff\xff\xff\xff\xff" /* 10 */
+  "\xff\xff\xff\xff\xff" /* 15 */
+  "\xff\xff\xff\xff\xff" /* 20 */
+  "\xff\xff\xff\xff\xff" /* 25 */
+  "\xff\xff\xff\xff\xff" /* 30 */
+  "\xff\xff\xff\xff\xff" /* 35 */
+  "\xff\xff\xff\xff\xff" /* 40 */
+  "\x3e\xff\xff\x34\x35" /* 45 */
+  "\x36\x37\x38\x39\x3a" /* 50 */
+  "\x3b\x3c\x3d\xff\xff" /* 55 */
+  "\xff\xff\xff\xff\xff" /* 60 */
+  "\x00\x01\x02\x03\x04" /* 65 A */
+  "\x05\x06\x07\x08\x09" /* 70 */
+  "\x0a\x0b\x0c\x0d\x0e" /* 75 */
+  "\x0f\x10\x11\x12\x13" /* 80 */
+  "\x14\x15\x16\x17\x18" /* 85 */
+  "\x19\xff\xff\xff\xff" /* 90 */
+  "\x3f\xff\x1a\x1b\x1c" /* 95 */
+  "\x1d\x1e\x1f\x20\x21" /* 100 */
+  "\x22\x23\x24\x25\x26" /* 105 */
+  "\x27\x28\x29\x2a\x2b" /* 110 */
+  "\x2c\x2d\x2e\x2f\x30" /* 115 */
+  "\x31\x32\x33\xff\xff" /* 120 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 135 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 145 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 165 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 175 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 195 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 205 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 225 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 235 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */
+};
+
+/* For encoding as a string */
+struct wbuf {
+       size_t off, len;
+       char *buf;
+};
+
+static void to_wbuf(const char *s, size_t len, void *vwbuf)
+{
+       struct wbuf *wbuf = vwbuf;
+
+       while (wbuf->off + len > wbuf->len)
+               tal_resize(&wbuf->buf, wbuf->len *= 2);
+       memcpy(wbuf->buf + wbuf->off, s, len);
+       wbuf->off += len;
+}
+
+/* For adding to sha256 */
+static void to_sha256(const char *s, size_t len, void *vshactx)
+{
+       struct sha256_ctx *shactx = vshactx;
+       sha256_update(shactx, s, len);
+}
+
+static void rune_altern_encode(const struct rune_altern *altern,
+                              void (*cb)(const char *s, size_t len,
+                                         void *arg),
+                              void *arg)
+{
+       char cond = altern->condition;
+       const char *p;
+
+       cb(altern->fieldname, strlen(altern->fieldname), arg);
+       cb(&cond, 1, arg);
+
+       p = altern->value;
+       for (;;) {
+               char esc[2] = { '\\' };
+               size_t len = strcspn(p, "\\|&");
+               cb(p, len, arg);
+               if (!p[len])
+                       break;
+               esc[1] = p[len];
+               cb(esc, 2, arg);
+               p += len + 1;
+       }
+}
+
+static void rune_restr_encode(const struct rune_restr *restr,
+                             void (*cb)(const char *s, size_t len,
+                                        void *arg),
+                             void *arg)
+{
+       for (size_t i = 0; i < tal_count(restr->alterns); i++) {
+               if (i != 0)
+                       cb("|", 1, arg);
+               rune_altern_encode(restr->alterns[i], cb, arg);
+       }
+}
+
+void rune_sha256_add_restr(struct sha256_ctx *shactx,
+                          struct rune_restr *restr)
+{
+       rune_restr_encode(restr, to_sha256, shactx);
+       rune_sha256_endmarker(shactx);
+}
+
+const char *rune_is_derived(const struct rune *source, const struct rune *rune)
+{
+       if (!runestr_eq(source->version, rune->version))
+               return "Version mismatch";
+
+       return rune_is_derived_anyversion(source, rune);
+}
+       
+const char *rune_is_derived_anyversion(const struct rune *source,
+                                      const struct rune *rune)
+{
+       struct sha256_ctx shactx;
+       size_t i;
+
+       if (tal_count(rune->restrs) < tal_count(source->restrs))
+               return "Fewer restrictions than master";
+
+       /* If we add the same restrictions to source rune, do we match? */
+       shactx = source->shactx;
+       for (i = 0; i < tal_count(rune->restrs); i++) {
+               /* First restrictions must be identical */
+               if (i < tal_count(source->restrs)) {
+                       if (!rune_restr_eq(source->restrs[i], rune->restrs[i]))
+                               return "Does not match master restrictions";
+               } else
+                       rune_sha256_add_restr(&shactx, rune->restrs[i]);
+       }
+
+       if (memcmp(shactx.s, rune->shactx.s, sizeof(shactx.s)) != 0)
+               return "Not derived from master";
+       return NULL;
+}
+
+static bool peek_char(const char *data, size_t len, char *c)
+{
+       if (len == 0)
+               return false;
+       *c = *data;
+       return true;
+}
+
+static void drop_char(const char **data, size_t *len)
+{
+       (*data)++;
+       (*len)--;
+}
+
+static void pull_invalid(const char **data, size_t *len)
+{
+       *data = NULL;
+       *len = 0;
+}
+
+static bool pull_char(const char **data, size_t *len, char *c)
+{
+       if (!peek_char(*data, *len, c)) {
+               pull_invalid(data, len);
+               return false;
+       }
+       drop_char(data, len);
+       return true;
+}
+
+bool rune_condition_is_valid(enum rune_condition cond)
+{
+       switch (cond) {
+       case RUNE_COND_IF_MISSING:
+       case RUNE_COND_EQUAL:
+       case RUNE_COND_NOT_EQUAL:
+       case RUNE_COND_BEGINS:
+       case RUNE_COND_ENDS:
+       case RUNE_COND_CONTAINS:
+       case RUNE_COND_INT_LESS:
+       case RUNE_COND_INT_GREATER:
+       case RUNE_COND_LEXO_BEFORE:
+       case RUNE_COND_LEXO_AFTER:
+       case RUNE_COND_COMMENT:
+               return true;
+       }
+       return false;
+}
+
+size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen)
+{
+       for (size_t i = 0; i < alternstrlen; i++) {
+               if (cispunct(alternstr[i]) && alternstr[i] != '_')
+                       return i;
+       }
+       return alternstrlen;
+}
+
+/* Sets *more on success: true if another altern follows */
+static struct rune_altern *rune_altern_decode(const tal_t *ctx,
+                                             const char **data, size_t *len,
+                                             bool *more)
+{
+       struct rune_altern *alt = tal(ctx, struct rune_altern);
+       char *value;
+       size_t strlen;
+       char c;
+
+        /* Swallow field up to possible conditional */
+       strlen = rune_altern_fieldname_len(*data, *len);
+       alt->fieldname = tal_strndup(alt, *data, strlen);
+       *data += strlen;
+       *len -= strlen;
+
+       /* Grab conditional */
+       if (!pull_char(data, len, &c) || !rune_condition_is_valid(c))
+               return tal_free(alt);
+
+       alt->condition = c;
+
+       /* Assign worst case. */
+       value = tal_arr(alt, char, *len + 1);
+       strlen = 0;
+       *more = false;
+       while (*len && pull_char(data, len, &c)) {
+               if (c == '|') {
+                       *more = true;
+                       break;
+               }
+               if (c == '&')
+                       break;
+
+               if (c == '\\' && !pull_char(data, len, &c))
+                       return tal_free(alt);
+               value[strlen++] = c;
+       }
+       value[strlen] = '\0';
+       tal_resize(&value, strlen + 1);
+       alt->value = value;
+       return alt;
+}
+
+static struct rune_restr *rune_restr_decode(const tal_t *ctx,
+                                           const char **data, size_t *len)
+{
+       struct rune_restr *restr = tal(ctx, struct rune_restr);
+       size_t num_alts = 0;
+       bool more;
+
+       /* Must have at least one! */
+       restr->alterns = tal_arr(restr, struct rune_altern *, 0);
+       do {
+               struct rune_altern *alt;
+
+               alt = rune_altern_decode(restr, data, len, &more);
+               if (!alt)
+                       return tal_free(restr);
+               tal_resize(&restr->alterns, num_alts+1);
+               restr->alterns[num_alts++] = alt;
+       } while (more);
+       return restr;
+}
+
+static struct rune *from_string(const tal_t *ctx,
+                               const char *str,
+                               const u8 *hash32)
+{
+       size_t len = strlen(str);
+       struct rune *rune = tal(ctx, struct rune);
+
+       /* Now count up how many bytes we should have hashed: secret uses
+        * first block. */
+       rune->shactx.bytes = 64;
+
+       rune->restrs = tal_arr(rune, struct rune_restr *, 0);
+       rune->unique_id = NULL;
+       rune->version = NULL;
+
+       while (len) {
+               struct rune_restr *restr;
+               restr = rune_restr_decode(rune, &str, &len);
+               if (!restr)
+                       return tal_free(rune);
+               if (!rune_add_restr(rune, restr))
+                       return tal_free(rune);
+       }
+
+       /* Now we replace with canned hash state */
+       memcpy(rune->shactx.s, hash32, 32);
+       for (size_t i = 0; i < 8; i++)
+               rune->shactx.s[i] = be32_to_cpu(rune->shactx.s[i]);
+
+       return rune;
+}
+
+struct rune_restr *rune_restr_from_string(const tal_t *ctx,
+                                         const char *str,
+                                         size_t len)
+{
+       struct rune_restr *restr;
+
+       restr = rune_restr_decode(NULL, &str, &len);
+       /* Don't allow trailing chars */
+       if (restr && len != 0)
+               restr = tal_free(restr);
+       return tal_steal(ctx, restr);
+}
+
+static void to_string(struct wbuf *wbuf, const struct rune *rune, u8 *hash32)
+{
+       /* Copy hash in big-endian */
+       for (size_t i = 0; i < 8; i++) {
+               be32 v = cpu_to_be32(rune->shactx.s[i]);
+               memcpy(hash32 + i*4, &v, sizeof(v));
+       }
+
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               if (i != 0)
+                       to_wbuf("&", 1, wbuf);
+               rune_restr_encode(rune->restrs[i], to_wbuf, wbuf);
+       }
+       to_wbuf("", 1, wbuf);
+}
+
+struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len)
+{
+       size_t blen;
+       u8 *data;
+       struct rune *rune;
+
+       data = tal_arr(NULL, u8, base64_decoded_length(len) + 1);
+
+       blen = base64_decode_using_maps(&base64_maps_urlsafe,
+                                      (char *)data, tal_bytelen(data),
+                                      str, len);
+       if (blen == -1)
+               goto fail;
+
+       if (blen < 32)
+               goto fail;
+
+       data[blen] = '\0';
+       /* Sanity check that it's a valid string! */
+       if (strlen((char *)data + 32) != blen - 32)
+               goto fail;
+
+       rune = from_string(ctx, (const char *)data + 32, data);
+       tal_free(data);
+       return rune;
+
+fail:
+       tal_free(data);
+       return NULL;
+}
+
+struct rune *rune_from_base64(const tal_t *ctx, const char *str)
+{
+       return rune_from_base64n(ctx, str, strlen(str));
+}
+
+char *rune_to_base64(const tal_t *ctx, const struct rune *rune)
+{
+       u8 hash32[32];
+       char *ret;
+       size_t ret_len;
+       struct wbuf wbuf;
+
+       /* We're going to prepend hash */
+       wbuf.off = sizeof(hash32);
+       wbuf.len = 64;
+       wbuf.buf = tal_arr(NULL, char, wbuf.len);
+
+       to_string(&wbuf, rune, hash32);
+       /* Prepend hash */
+       memcpy(wbuf.buf, hash32, sizeof(hash32));
+
+       ret = tal_arr(ctx, char, base64_encoded_length(wbuf.off) + 1);
+       ret_len = base64_encode_using_maps(&base64_maps_urlsafe,
+                                          ret, tal_bytelen(ret),
+                                          wbuf.buf, wbuf.off - 1);
+       ret[ret_len] = '\0';
+       tal_free(wbuf.buf);
+       return ret;
+}
+
+struct rune *rune_from_string(const tal_t *ctx, const char *str)
+{
+       u8 hash[32];
+       if (!hex_decode(str, 64, hash, sizeof(hash)))
+               return NULL;
+       if (str[64] != ':')
+               return NULL;
+       return from_string(ctx, str + 65, hash);
+}
+
+char *rune_to_string(const tal_t *ctx, const struct rune *rune)
+{
+       u8 hash32[32];
+       struct wbuf wbuf;
+
+       /* We're going to prepend hash (in hex), plus colon */
+       wbuf.off = sizeof(hash32) * 2 + 1;
+       wbuf.len = 128;
+       wbuf.buf = tal_arr(ctx, char, wbuf.len);
+
+       to_string(&wbuf, rune, hash32);
+       hex_encode(hash32, sizeof(hash32), wbuf.buf, sizeof(hash32) * 2 + 1);
+       wbuf.buf[sizeof(hash32) * 2] = ':';
+       return wbuf.buf;
+}
diff --git a/ccan/rune/internal.h b/ccan/rune/internal.h
new file mode 100644 (file)
index 0000000..e4de06c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef CCAN_RUNE_INTERNAL_H
+#define CCAN_RUNE_INTERNAL_H
+/* MIT (BSD) license - see LICENSE file for details */
+void rune_sha256_endmarker(struct sha256_ctx *shactx);
+void rune_sha256_add_restr(struct sha256_ctx *shactx,
+                       struct rune_restr *restr);
+bool runestr_eq(const char *a, const char *b);
+#endif /* CCAN_RUNE_INTERNAL_H */
diff --git a/ccan/rune/rune.c b/ccan/rune/rune.c
new file mode 100644 (file)
index 0000000..84296c6
--- /dev/null
@@ -0,0 +1,491 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include "config.h"
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <ccan/endian/endian.h>
+#include <ccan/mem/mem.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/rune/rune.h>
+#include <ccan/rune/internal.h>
+
+/* Helper to produce an id field */
+static struct rune_restr *unique_id_restr(const tal_t *ctx,
+                                         const char *unique_id,
+                                         const char *version)
+{
+       const char *id;
+       struct rune_restr *restr;
+
+       assert(!strchr(unique_id, '-'));
+       if (version)
+               id = tal_fmt(NULL, "%s-%s", unique_id, version);
+       else
+               id = tal_strdup(NULL, unique_id);
+
+       restr = rune_restr_new(ctx);
+        /* We use the empty field for this, since it's always present. */
+       rune_restr_add_altern(restr,
+                             take(rune_altern_new(NULL, "", '=', take(id))));
+       return restr;
+}
+
+/* We pad between fields with something identical to the SHA end marker */
+void rune_sha256_endmarker(struct sha256_ctx *shactx)
+{
+       static const unsigned char pad[64] = {0x80};
+       be64 sizedesc;
+
+       sizedesc = cpu_to_be64((uint64_t)shactx->bytes << 3);
+       /* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
+       sha256_update(shactx, pad, 1 + ((128 - 8 - (shactx->bytes % 64) - 1) % 64));
+       /* Add number of bits of data (big endian) */
+       sha256_update(shactx, &sizedesc, 8);
+}
+
+struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len,
+                     const char *version)
+{
+       struct rune *rune = tal(ctx, struct rune);
+        assert(secret_len + 1 + 8 <= 64);
+
+       if (version)
+               rune->version = tal_strdup(rune, version);
+       else
+               rune->version = NULL;
+       rune->unique_id = NULL;
+       sha256_init(&rune->shactx);
+       sha256_update(&rune->shactx, secret, secret_len);
+       rune_sha256_endmarker(&rune->shactx);
+       rune->restrs = tal_arr(rune, struct rune_restr *, 0);
+       return rune;
+}
+
+struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES)
+{
+       struct rune *dup;
+
+       if (taken(rune))
+               return tal_steal(ctx, (struct rune *)rune);
+
+       dup = tal_dup(ctx, struct rune, rune);
+       dup->restrs = tal_arr(dup, struct rune_restr *, tal_count(rune->restrs));
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               dup->restrs[i] = rune_restr_dup(dup->restrs,
+                                               rune->restrs[i]);
+       }
+       return dup;
+}
+
+struct rune *rune_derive_start(const tal_t *ctx,
+                              const struct rune *master,
+                              const char *unique_id TAKES)
+{
+       struct rune *rune = rune_dup(ctx, master);
+
+        /* If they provide a unique_id, it goes first. */
+       if (unique_id) {
+               if (taken(unique_id))
+                       rune->unique_id = tal_steal(rune, unique_id);
+               else
+                       rune->unique_id = tal_strdup(rune, unique_id);
+               
+               rune_add_restr(rune, take(unique_id_restr(NULL,
+                                                         rune->unique_id,
+                                                         rune->version)));
+       } else {
+               assert(!rune->version);
+       }
+       return rune;
+}
+
+struct rune_altern *rune_altern_new(const tal_t *ctx,
+                                   const char *fieldname TAKES,
+                                   enum rune_condition condition,
+                                   const char *value TAKES)
+{
+       struct rune_altern *altern = tal(ctx, struct rune_altern);
+       altern->condition = condition;
+       altern->fieldname = tal_strdup(altern, fieldname);
+       altern->value = tal_strdup(altern, value);
+       return altern;
+}
+
+struct rune_altern *rune_altern_dup(const tal_t *ctx,
+                                   const struct rune_altern *altern TAKES)
+{
+       struct rune_altern *dup;
+
+       if (taken(altern))
+               return tal_steal(ctx, (struct rune_altern *)altern);
+       dup = tal(ctx, struct rune_altern);
+       dup->condition = altern->condition;
+       dup->fieldname = tal_strdup(dup, altern->fieldname);
+       dup->value = tal_strdup(dup, altern->value);
+       return dup;
+}
+
+struct rune_restr *rune_restr_dup(const tal_t *ctx,
+                                 const struct rune_restr *restr TAKES)
+{
+       struct rune_restr *dup;
+       size_t num_altern;
+
+       if (taken(restr))
+               return tal_steal(ctx, (struct rune_restr *)restr);
+
+       num_altern = tal_count(restr->alterns);
+       dup = tal(ctx, struct rune_restr);
+       dup->alterns = tal_arr(dup, struct rune_altern *, num_altern);
+       for (size_t i = 0; i < num_altern; i++) {
+               dup->alterns[i] = rune_altern_dup(dup->alterns,
+                                                 restr->alterns[i]);
+       }
+       return dup;
+}
+
+struct rune_restr *rune_restr_new(const tal_t *ctx)
+{
+       struct rune_restr *restr = tal(ctx, struct rune_restr);
+       restr->alterns = tal_arr(restr, struct rune_altern *, 0);
+       return restr;
+}
+
+void rune_restr_add_altern(struct rune_restr *restr,
+                          const struct rune_altern *alt TAKES)
+{
+       size_t num = tal_count(restr->alterns);
+
+       tal_resize(&restr->alterns, num+1);
+       restr->alterns[num] = rune_altern_dup(restr->alterns, alt);
+}
+
+static bool is_unique_id(const struct rune_altern *alt)
+{
+       return streq(alt->fieldname, "");
+}
+       
+/* Return unique_id if valid, and sets *version */
+static const char *extract_unique_id(const tal_t *ctx,
+                                    const struct rune_altern *alt,
+                                    const char **version)
+{
+       size_t len;
+       /* Condition must be '='! */
+       if (alt->condition != '=')
+               return NULL;
+
+       len = strcspn(alt->value, "-");
+       if (alt->value[len])
+               *version = tal_strdup(ctx, alt->value + len + 1);
+       else
+               *version = NULL;
+       return tal_strndup(ctx, alt->value, len);
+}
+
+bool rune_add_restr(struct rune *rune,
+                   const struct rune_restr *restr TAKES)
+{
+       size_t num = tal_count(rune->restrs);
+
+       /* An empty fieldname is additional correctness checks */
+       for (size_t i = 0; i < tal_count(restr->alterns); i++) {
+               if (!is_unique_id(restr->alterns[i]))
+                       continue;
+
+               /* Must be the only alternative */
+               if (tal_count(restr->alterns) != 1)
+                       goto fail;
+               /* Must be the first restriction */
+               if (num != 0)
+                       goto fail;
+
+               rune->unique_id = extract_unique_id(rune,
+                                                   restr->alterns[i],
+                                                   &rune->version);
+               if (!rune->unique_id)
+                       goto fail;
+       }
+
+       tal_resize(&rune->restrs, num+1);
+       rune->restrs[num] = rune_restr_dup(rune->restrs, restr);
+
+       rune_sha256_add_restr(&rune->shactx, rune->restrs[num]);
+       return true;
+
+fail:
+       if (taken(restr))
+               tal_free(restr);
+       return false;
+}
+
+static const char *rune_restr_test(const tal_t *ctx,
+                                  const struct rune *rune,
+                                  const struct rune_restr *restr,
+                                  const char *(*check)(const tal_t *ctx,
+                                                       const struct rune *rune,
+                                                       const struct rune_altern *alt,
+                                                       void *arg),
+                                  void *arg)
+{
+       size_t num = tal_count(restr->alterns);
+       const char **errs = tal_arr(NULL, const char *, num);
+       char *err;
+
+       /* Only one alternative has to pass! */
+       for (size_t i = 0; i < num; i++) {
+               errs[i] = check(errs, rune, restr->alterns[i], arg);
+               if (!errs[i]) {
+                       tal_free(errs);
+                       return NULL;
+               }
+       }
+
+       err = tal_fmt(ctx, "%s", errs[0]);
+       for (size_t i = 1; i < num; i++)
+               tal_append_fmt(&err, " AND %s", errs[i]);
+       tal_free(errs);
+       return err;
+}
+
+static const char *cond_test(const tal_t *ctx,
+                            const struct rune_altern *alt,
+                            const char *complaint,
+                            bool cond)
+{
+       if (cond)
+               return NULL;
+
+       return tal_fmt(ctx, "%s %s %s", alt->fieldname, complaint, alt->value);
+}
+
+static const char *integer_compare_valid(const tal_t *ctx,
+                                        const s64 *fieldval_int,
+                                        const struct rune_altern *alt,
+                                        s64 *runeval_int)
+{
+       long l;
+       char *p;
+
+       if (!fieldval_int)
+               return tal_fmt(ctx, "%s is not an integer field",
+                              alt->fieldname);
+
+       errno = 0;
+       l = strtol(alt->value, &p, 10);
+       if (p == alt->value
+           || *p
+           || ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE))
+               return tal_fmt(ctx, "%s is not a valid integer", alt->value);
+
+       *runeval_int = l;
+       return NULL;
+}
+
+static int lexo_order(const char *fieldval_str,
+                     size_t fieldval_strlen,
+                     const char *alt)
+{
+       int ret = strncmp(fieldval_str, alt, fieldval_strlen);
+
+       /* If alt is same but longer, fieldval is < */
+       if (ret == 0 && strlen(alt) > fieldval_strlen)
+               ret = -1;
+       return ret;
+}
+
+static const char *rune_alt_single(const tal_t *ctx,
+                                  const struct rune_altern *alt,
+                                  const char *fieldval_str,
+                                  size_t fieldval_strlen,
+                                  const s64 *fieldval_int)
+{
+       char strfield[STR_MAX_CHARS(s64) + 1];
+       s64 runeval_int = 0 /* gcc v9.4.0 gets upset with uninitiaized var at -O3 */;
+       const char *err;
+
+       /* Caller can't set both! */
+       if (fieldval_int) {
+               assert(!fieldval_str);
+               sprintf(strfield, "%"PRIi64, *fieldval_int);
+               fieldval_str = strfield;
+               fieldval_strlen = strlen(strfield);
+       }
+
+       switch (alt->condition) {
+       case RUNE_COND_IF_MISSING:
+               if (!fieldval_str)
+                       return NULL;
+               return tal_fmt(ctx, "%s is present", alt->fieldname);
+       case RUNE_COND_EQUAL:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is not equal to",
+                                memeqstr(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_NOT_EQUAL:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to",
+                                !memeqstr(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_BEGINS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not start with",
+                                memstarts_str(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_ENDS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not end with",
+                                memends_str(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_CONTAINS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not contain",
+                                memmem(fieldval_str, fieldval_strlen,
+                                       alt->value, strlen(alt->value)));
+       case RUNE_COND_INT_LESS:
+               err = integer_compare_valid(ctx, fieldval_int,
+                                           alt, &runeval_int);
+               if (err)
+                       return err;
+               return cond_test(ctx, alt, "is greater or equal to",
+                                *fieldval_int < runeval_int);
+       case RUNE_COND_INT_GREATER:
+               err = integer_compare_valid(ctx, fieldval_int,
+                                           alt, &runeval_int);
+               if (err)
+                       return err;
+               return cond_test(ctx, alt, "is less or equal to",
+                                *fieldval_int > runeval_int);
+       case RUNE_COND_LEXO_BEFORE:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to or ordered after",
+                                lexo_order(fieldval_str, fieldval_strlen, alt->value) < 0);
+       case RUNE_COND_LEXO_AFTER:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to or ordered before",
+                                lexo_order(fieldval_str, fieldval_strlen, alt->value) > 0);
+       case RUNE_COND_COMMENT:
+               return NULL;
+       }
+       /* We should never create any other values! */
+       abort();
+}
+
+const char *rune_alt_single_str(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               const char *fieldval_str,
+                               size_t fieldval_strlen)
+{
+       return rune_alt_single(ctx, alt, fieldval_str, fieldval_strlen, NULL);
+}
+
+const char *rune_alt_single_int(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               s64 fieldval_int)
+{
+       return rune_alt_single(ctx, alt, NULL, 0, &fieldval_int);
+}
+
+const char *rune_alt_single_missing(const tal_t *ctx,
+                                   const struct rune_altern *alt)
+{
+       return rune_alt_single(ctx, alt, NULL, 0, NULL);
+}
+
+const char *rune_meets_criteria_(const tal_t *ctx,
+                                const struct rune *rune,
+                                const char *(*check)(const tal_t *ctx,
+                                                     const struct rune *rune,
+                                                     const struct rune_altern *alt,
+                                                     void *arg),
+                                void *arg)
+{
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               const char *err;
+
+               /* Don't "check" unique id */
+               if (i == 0 && is_unique_id(rune->restrs[i]->alterns[0]))
+                       continue;
+               
+               err = rune_restr_test(ctx, rune, rune->restrs[i], check, arg);
+               if (err)
+                       return err;
+       }
+       return NULL;
+}
+
+const char *rune_test_(const tal_t *ctx,
+                      const struct rune *master,
+                      const struct rune *rune,
+                      const char *(*check)(const tal_t *ctx,
+                                           const struct rune *rune,
+                                           const struct rune_altern *alt,
+                                           void *arg),
+                      void *arg)
+{
+       const char *err;
+
+       err = rune_is_derived(master, rune);
+       if (err)
+               return err;
+       return rune_meets_criteria_(ctx, rune, check, arg);
+}
+
+bool rune_altern_eq(const struct rune_altern *alt1,
+                   const struct rune_altern *alt2)
+{
+       return alt1->condition == alt2->condition
+               && streq(alt1->fieldname, alt2->fieldname)
+               && streq(alt1->value, alt2->value);
+}
+
+bool rune_restr_eq(const struct rune_restr *rest1,
+                  const struct rune_restr *rest2)
+{
+       if (tal_count(rest1->alterns) != tal_count(rest2->alterns))
+               return false;
+
+       for (size_t i = 0; i < tal_count(rest1->alterns); i++)
+               if (!rune_altern_eq(rest1->alterns[i], rest2->alterns[i]))
+                       return false;
+       return true;
+}
+
+/* Equal, as in both NULL, or both non-NULL and matching */
+bool runestr_eq(const char *a, const char *b)
+{
+       if (a) {
+               if (!b)
+                       return false;
+               return streq(a, b);
+       } else
+               return b == NULL;
+}
+
+bool rune_eq(const struct rune *rune1, const struct rune *rune2)
+{
+       if (!runestr_eq(rune1->unique_id, rune2->unique_id))
+               return false;
+       if (!runestr_eq(rune1->version, rune2->version))
+               return false;
+
+       if (memcmp(rune1->shactx.s, rune2->shactx.s, sizeof(rune1->shactx.s)))
+               return false;
+       if (rune1->shactx.bytes != rune2->shactx.bytes)
+               return false;
+       if (memcmp(rune1->shactx.buf.u8, rune2->shactx.buf.u8,
+                  rune1->shactx.bytes % 64))
+               return false;
+
+       if (tal_count(rune1->restrs) != tal_count(rune2->restrs))
+               return false;
+
+       for (size_t i = 0; i < tal_count(rune1->restrs); i++)
+               if (!rune_restr_eq(rune1->restrs[i], rune2->restrs[i]))
+                       return false;
+       return true;
+}
diff --git a/ccan/rune/rune.h b/ccan/rune/rune.h
new file mode 100644 (file)
index 0000000..c373269
--- /dev/null
@@ -0,0 +1,400 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_RUNE_RUNE_H
+#define CCAN_RUNE_RUNE_H
+#include <ccan/crypto/sha256/sha256.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <ccan/tal/tal.h>
+#include <ccan/short_types/short_types.h>
+
+/* A rune is a series of restrictions. */
+struct rune {
+       /* unique_id (if any) */
+       const char *unique_id;
+       /* Version (if any) */
+       const char *version;
+
+       /* SHA-2 256 of restrictions so far. */
+       struct sha256_ctx shactx;
+       /* Length given by tal_count() */
+       struct rune_restr **restrs;
+};
+
+/* A restriction is one or more alternatives (altern) */
+struct rune_restr {
+       /* Length given by tal_count() */
+       struct rune_altern **alterns;
+};
+
+enum rune_condition {
+       RUNE_COND_IF_MISSING = '!',
+       RUNE_COND_EQUAL = '=',
+       RUNE_COND_NOT_EQUAL = '/',
+       RUNE_COND_BEGINS = '^',
+       RUNE_COND_ENDS = '$',
+       RUNE_COND_CONTAINS = '~',
+       RUNE_COND_INT_LESS = '<',
+       RUNE_COND_INT_GREATER = '>',
+       RUNE_COND_LEXO_BEFORE = '{',
+       RUNE_COND_LEXO_AFTER = '}',
+       RUNE_COND_COMMENT = '#',
+};
+
+/* An alternative is a utf-8 fieldname, a condition, and a value */
+struct rune_altern {
+       enum rune_condition condition;
+       /* Strings. */
+       const char *fieldname, *value;
+};
+
+/**
+ * rune_new - Create an unrestricted rune from this secret.
+ * @ctx: tal context, or NULL.  Freeing @ctx will free the returned rune.
+ * @secret: secret bytes.
+ * @secret_len: number of @secret bytes (must be 55 bytes or less)
+ * @version: if non-NULL, sets a version for this rune.
+ *
+ * This allocates a new, unrestricted rune (sometimes called a master rune).
+ *
+ * Setting a version allows for different interpretations of a rune if
+ * things change in future, at cost of some space when it's used.
+ *
+ * Example:
+ *  u8 secret[16];
+ *  struct rune *master;
+ *
+ *  // A secret determined with a fair die roll!
+ *  memset(secret, 5, sizeof(secret));
+ *  master = rune_new(NULL, secret, sizeof(secret), NULL);
+ *  assert(master);
+ */
+struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len,
+                     const char *version);
+
+/**
+ * rune_derive_start - Copy master rune, add a unique id.
+ * @ctx: context to allocate rune off
+ * @master: master rune.
+ * @unique_id: unique id; can be NULL, but that's not recommended.
+ *
+ * It's usually recommended to assign each rune a unique_id, so that
+ * specific runes can be blacklisted later (otherwise you need to disable
+ * all runes).  This enlarges the rune string by '=<unique_id>' however.
+ *
+ * The rune version will be the same as the master: if that's non-zero,
+ * you *must* set unique_id.
+ *
+ * @unique_id cannot contain '-'.
+ *
+ * Example:
+ *  struct rune *rune;
+ *  // In reality, some global incrementing variable.
+ *  const char *id = "1";
+ *  rune = rune_derive_start(NULL, master, id);
+ *  assert(rune);
+ */
+struct rune *rune_derive_start(const tal_t *ctx,
+                              const struct rune *master,
+                              const char *unique_id);
+
+/**
+ * rune_dup - Copy a rune.
+ * @ctx: tal context, or NULL.
+ * @altern: the altern to copy.
+ *
+ * If @altern is take(), then simply returns it, otherwise copies.
+ */
+struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES);
+
+/**
+ * rune_altern_new - Create a new alternative.
+ * @ctx: tal context, or NULL.  Freeing @ctx will free the returned altern.
+ * @fieldname: the UTF-8 field for the altern.  You can only have
+ *             alphanumerics, '.', '-' and '_' here.
+ * @condition: the condition, defined above.
+ * @value: the value for comparison; use "" if you don't care.  Any UTF-8 value
+ *         is allowed.
+ *
+ * An altern is the basis of rune restrictions (technically, a restriction
+ * is one or more alterns, but it's often just one).
+ *
+ * Example:
+ *  struct rune_altern *a1, *a2;
+ *  a1 = rune_altern_new(NULL, "val", RUNE_COND_EQUAL, "7");
+ *  a2 = rune_altern_new(NULL, "val2", '>', "-1");
+ *  assert(a1 && a2);
+ */
+struct rune_altern *rune_altern_new(const tal_t *ctx,
+                                   const char *fieldname TAKES,
+                                   enum rune_condition condition,
+                                   const char *value TAKES);
+
+/**
+ * rune_altern_dup - copy an alternative.
+ * @ctx: tal context, or NULL.
+ * @altern: the altern to copy.
+ *
+ * If @altern is take(), then simply returns it, otherwise copies.
+ */
+struct rune_altern *rune_altern_dup(const tal_t *ctx,
+                                   const struct rune_altern *altern TAKES);
+
+/**
+ * rune_restr_new - Create a new (empty) restriction.
+ * @ctx: tal context, or NULL.  Freeing @ctx will free the returned restriction.
+ *
+ * Example:
+ *  struct rune_restr *restr = rune_restr_new(NULL);
+ *  assert(restr);
+ */
+struct rune_restr *rune_restr_new(const tal_t *ctx);
+
+/**
+ * rune_restr_dup - copy a restr.
+ * @ctx: tal context, or NULL.
+ * @restr: the restr to copy.
+ *
+ * If @resttr is take(), then simply returns it, otherwise copies.
+ */
+struct rune_restr *rune_restr_dup(const tal_t *ctx,
+                                 const struct rune_restr *restr TAKES);
+
+/**
+ * rune_restr_add_altern - add an altern to this restriction
+ * @restr: the restriction to add to
+ * @alt: the altern.
+ *
+ * If the alt is take(alt) then the alt will be owned by the restriction,
+ * otherwise it's copied.
+ *
+ * Example:
+ *  rune_restr_add_altern(restr, take(a1));
+ *  rune_restr_add_altern(restr, take(a2));
+ */
+void rune_restr_add_altern(struct rune_restr *restr,
+                          const struct rune_altern *alt TAKES);
+
+/**
+ * rune_add_restr - add a restriction to this rune
+ * @rune: the rune to add to.
+ * @restr: the (non-empty) restriction.
+ *
+ * If the alt is take(alt) then the alt will be owned by the restr,
+ * otherwise it's copied (and all its children are copied!).
+ *
+ * This fails (and returns false) if restr tries to set unique_id/version
+ * and is not the first restriction, or has more than one alternative,
+ * or uses a non '=' condition.
+ *
+ * Example:
+ *  rune_add_restr(rune, take(restr));
+ */
+bool rune_add_restr(struct rune *rune,
+                   const struct rune_restr *restr TAKES);
+
+/**
+ * rune_altern_eq - are two rune_altern equivalent?
+ * @alt1: the first
+ * @alt2: the second
+ */
+bool rune_altern_eq(const struct rune_altern *alt1,
+                   const struct rune_altern *alt2);
+
+/**
+ * rune_restr_eq - are two rune_restr equivalent?
+ * @rest1: the first
+ * @rest2: the second
+ */
+bool rune_restr_eq(const struct rune_restr *rest1,
+                  const struct rune_restr *rest2);
+
+/**
+ * rune_eq - are two runes equivalent?
+ * @rest1: the first
+ * @rest2: the second
+ */
+bool rune_eq(const struct rune *rune1, const struct rune *rune2);
+
+/**
+ * rune_alt_single_str - helper to implement check().
+ * @ctx: context to allocate any error return from.
+ * @alt: alternative to test.
+ * @fieldval_str: field value as a string.
+ * @fieldval_strlen: length of @fieldval_str
+ */
+const char *rune_alt_single_str(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               const char *fieldval_str,
+                               size_t fieldval_strlen);
+
+/**
+ * rune_alt_single_int - helper to implement check().
+ * @ctx: context to allocate any error return from.
+ * @alt: alternative to test.
+ * @fieldval_int: field value as an integer.
+ */
+const char *rune_alt_single_int(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               s64 fieldval_int);
+
+/**
+ * rune_alt_single_missing - helper to implement check().
+ * @ctx: context to allocate any error return from.
+ * @alt: alternative to test.
+ *
+ * Use this if alt->fieldname is unknown (it could still pass, if
+ * the test is that the fieldname is missing).
+ */
+const char *rune_alt_single_missing(const tal_t *ctx,
+                                   const struct rune_altern *alt);
+
+
+/**
+ * rune_is_derived - is a rune derived from this other rune?
+ * @source: the base rune (usually the master rune)
+ * @rune: the rune to check.
+ *
+ * This is the first part of "is this rune valid?": does the cryptography
+ * check out, such that they validly made the rune from this source rune?
+ *
+ * It also checks that the versions match: if you want to allow more than
+ * one version, see rune_is_derived_anyversion.
+ */
+const char *rune_is_derived(const struct rune *source, const struct rune *rune);
+
+/**
+ * rune_is_derived_anyversion - is a rune derived from this other rune?
+ * @source: the base rune (usually the master rune)
+ * @rune: the rune to check.
+ *
+ * This does not check source->version against rune->version: if you issue
+ * different rune versions you will need to check that yourself.
+ */
+const char *rune_is_derived_anyversion(const struct rune *source,
+                                      const struct rune *rune);
+
+/**
+ * rune_meets_criteria - do we meet the criteria specified by the rune?
+ * @ctx: the tal context to allocate the returned error off.
+ * @rune: the rune to check.
+ * @check: the callback to check values
+ * @arg: data to hand to @check
+ *
+ * This is the second part of "is this rune valid?".
+ */
+const char *rune_meets_criteria_(const tal_t *ctx,
+                                const struct rune *rune,
+                                const char *(*check)(const tal_t *ctx,
+                                                     const struct rune *rune,
+                                                     const struct rune_altern *alt,
+                                                     void *arg),
+                                void *arg);
+
+/* Typesafe wrapper */
+#define rune_meets_criteria(ctx, rune, check, arg)                     \
+       rune_meets_criteria_(typesafe_cb_preargs(const char *, void *,  \
+                                                (ctx), (rune),         \
+                                                (check), (arg),        \
+                                                const tal_t *,         \
+                                                const struct rune *,   \
+                                                const struct rune_altern *), \
+                            (arg))
+
+/**
+ * rune_test - is a rune authorized?
+ * @ctx: the tal context to allocate @errstr off.
+ * @master: the master rune created from secret.
+ * @rune: the rune to check.
+ * @errstr: if non-NULL, descriptive string of failure.
+ * @get: the callback to get values
+ * @arg: data to hand to callback
+ *
+ * Simple call for rune_is_derived() and rune_meets_criteria().  If
+ * it's not OK, returns non-NULL.
+ */
+const char *rune_test_(const tal_t *ctx,
+                      const struct rune *master,
+                      const struct rune *rune,
+                      const char *(*check)(const tal_t *ctx,
+                                           const struct rune *rune,
+                                           const struct rune_altern *alt,
+                                           void *arg),
+                      void *arg);
+
+/* Typesafe wrapper */
+#define rune_test(ctx_, master_, rune_, check_, arg_)                  \
+       rune_test_((ctx_), (master_), (rune_),                          \
+                  typesafe_cb_preargs(const char *, void *,            \
+                                      (check_), (arg_),                \
+                                      const tal_t *,                   \
+                                      const struct rune *,             \
+                                      const struct rune_altern *),     \
+                  (arg_))
+
+
+/**
+ * rune_from_base64 - convert base64 string to rune.
+ * @ctx: context to allocate rune off.
+ * @str: base64 string.
+ *
+ * Returns NULL if it's malformed.
+ */
+struct rune *rune_from_base64(const tal_t *ctx, const char *str);
+
+/**
+ * rune_from_base64n - convert base64 string to rune.
+ * @ctx: context to allocate rune off.
+ * @str: base64 string.
+ * @len: length of @str.
+ *
+ * Returns NULL if it's malformed.
+ */
+struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len);
+
+/**
+ * rune_to_base64 - convert run to base64 string.
+ * @ctx: context to allocate rune off.
+ * @rune: the rune.
+ *
+ * Only returns NULL if you've allowed tal allocations to return NULL.
+ */
+char *rune_to_base64(const tal_t *ctx, const struct rune *rune);
+
+/**
+ * This is a much more convenient working form.
+ */
+struct rune *rune_from_string(const tal_t *ctx, const char *str);
+char *rune_to_string(const tal_t *ctx, const struct rune *rune);
+
+/**
+ * rune_restr_from_string - convenience routine to parse a single restriction.
+ * @ctx: context to allocate rune off.
+ * @str: the string of form "<field><cond><val>[|<field><cond><val>]*"
+ * @len: the length of @str.
+ *
+ * This is useful for writing simple tests and making simple runes.
+ */
+struct rune_restr *rune_restr_from_string(const tal_t *ctx,
+                                         const char *str,
+                                         size_t len);
+
+/**
+ * rune_condition_is_valid: is this a valid condition?
+ * @cond: potential condition character.
+ *
+ * Returns true if it's one of enum rune_condition.
+ */
+bool rune_condition_is_valid(enum rune_condition cond);
+
+/**
+ * rune_altern_fieldname_len: how much of this string is condition?
+ * @alternstr: potential alternative string
+ * @alternstrlen: length
+ *
+ * This helps parsing your own runes.
+ *
+ * Returns the first possible condition (check with rune_condition_is_valid)
+ * or alternstrlen if none found.
+ */
+size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen);
+
+#endif /* CCAN_RUNE_RUNE_H */
diff --git a/ccan/rune/test/run-alt-lexicographic-order.c b/ccan/rune/test/run-alt-lexicographic-order.c
new file mode 100644 (file)
index 0000000..a37ee58
--- /dev/null
@@ -0,0 +1,33 @@
+#include <ccan/rune/rune.c>
+#include <ccan/rune/coding.c>
+#include <ccan/tal/str/str.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       const char *str = "test string";
+       plan_tests(strlen(str) * strlen(str));
+
+       for (size_t i = 0; str[i]; i++) {
+               char *stra = strdup(str);
+               stra[i] = '\0';
+               for (size_t j = 0; str[j]; j++) {
+                       char *strb = strdup(str);
+                       strb[j] = '\0';
+                       int lexo, strc;
+
+                       lexo = lexo_order(str, i, strb);
+                       strc = strcmp(stra, strb);
+                       if (strc > 0)
+                               ok1(lexo > 0);
+                       else if (strc < 0)
+                               ok1(lexo < 0);
+                       else
+                               ok1(lexo == 0);
+                       free(strb);
+               }
+               free(stra);
+       }
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/rune/test/run-altern-escape.c b/ccan/rune/test/run-altern-escape.c
new file mode 100644 (file)
index 0000000..550cafa
--- /dev/null
@@ -0,0 +1,37 @@
+#include <ccan/rune/rune.c>
+#include <ccan/rune/coding.c>
+#include <ccan/tal/grab_file/grab_file.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       static const u8 secret_zero[16];
+       struct rune *rune;
+       struct rune_restr *restr;
+       const tal_t *ctx = tal(NULL, char);
+
+       plan_tests(9);
+       restr = rune_restr_from_string(ctx, "desc=@tipjar\\|jb55@sendsats.lol",
+                                      strlen("desc=@tipjar\\|jb55@sendsats.lol"));
+       ok1(tal_count(restr->alterns) == 1);
+       ok1(restr->alterns[0]->condition == '=');
+       ok1(streq(restr->alterns[0]->fieldname, "desc"));
+       ok1(streq(restr->alterns[0]->value, "@tipjar|jb55@sendsats.lol"));
+
+       rune = rune_new(ctx, secret_zero, sizeof(secret_zero), NULL); 
+       rune_add_restr(rune, take(restr));
+
+       /* Converting via base64 should not change it! */
+       rune = rune_from_base64(ctx, rune_to_base64(ctx, rune));
+       ok1(tal_count(rune->restrs) == 1);
+       restr = rune->restrs[0];
+       ok1(tal_count(restr->alterns) == 1);
+       ok1(restr->alterns[0]->condition == '=');
+       ok1(streq(restr->alterns[0]->fieldname, "desc"));
+       ok1(streq(restr->alterns[0]->value, "@tipjar|jb55@sendsats.lol"));
+       
+       tal_free(ctx);
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/rune/test/run.c b/ccan/rune/test/run.c
new file mode 100644 (file)
index 0000000..d90b701
--- /dev/null
@@ -0,0 +1,127 @@
+#include <ccan/rune/rune.c>
+#include <ccan/rune/coding.c>
+#include <ccan/tal/grab_file/grab_file.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/tap/tap.h>
+
+static const char *check(const tal_t *ctx,
+                        const struct rune *rune,
+                        const struct rune_altern *alt,
+                        char **parts)
+{
+       const char *val = NULL;
+
+       for (size_t i = 1; parts[i]; i++) {
+               if (strstarts(parts[i], alt->fieldname)
+                   && parts[i][strlen(alt->fieldname)] == '=')
+                       val = parts[i] + strlen(alt->fieldname) + 1;
+       }
+
+       /* If it's an integer, hand it like that */
+       if (val) {
+               char *endp;
+               s64 v = strtol(val, &endp, 10);
+               if (*endp == '\0' && endp != val)
+                       return rune_alt_single_int(ctx, alt, v);
+               return rune_alt_single_str(ctx, alt, val, strlen(val));
+       }
+       return rune_alt_single_missing(ctx, alt);
+}
+
+int main(void)
+{
+       char *vecs;
+       char **lines;
+       static const u8 secret_zero[16];
+       struct rune *mr;
+
+       /* Test vector rune uses all-zero secret */
+       mr = rune_new(NULL, secret_zero, sizeof(secret_zero), NULL); 
+
+       /* Python runes library generates test vectors */
+       vecs = grab_file(mr, "test/test_vectors.csv");
+       assert(vecs);
+       lines = tal_strsplit(mr, take(vecs), "\n", STR_NO_EMPTY);
+
+       plan_tests(355);
+
+       for (size_t i = 0; lines[i]; i++) {
+               struct rune *rune1, *rune2;
+               char **parts;
+
+               parts = tal_strsplit(lines, lines[i], ",", STR_EMPTY_OK);
+               if (streq(parts[0], "VALID")) {
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_string(parts, parts[2]);
+                       ok1(rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(rune2);
+                       ok1(rune_eq(rune1, rune2));
+                       ok1(streq(rune_to_string(parts, rune2), parts[2]));
+                       ok1(streq(rune_to_base64(parts, rune1), parts[3]));
+                       ok1(rune_is_derived_anyversion(mr, rune1) == NULL);
+                       ok1(rune_is_derived_anyversion(mr, rune2) == NULL);
+
+                       if (parts[4]) {
+                               if (parts[5])
+                                       ok1(streq(rune1->version, parts[5]));
+                               ok1(streq(rune1->unique_id, parts[4]));
+                       } else {
+                               ok1(!rune1->version);
+                               ok1(!rune1->unique_id);
+                       }
+                       mr->version = NULL;
+               } else if (streq(parts[0], "DERIVE")) {
+                       struct rune_restr *restr;
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_base64(parts, parts[2]);
+                       ok1(rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(rune2);
+                       ok1(rune_is_derived_anyversion(mr, rune1) == NULL);
+                       ok1(rune_is_derived_anyversion(mr, rune2) == NULL);
+                       ok1(rune_is_derived_anyversion(rune1, rune2) == NULL);
+
+                       restr = rune_restr_new(NULL);
+                       for (size_t j = 4; parts[j]; j+=3) {
+                               struct rune_altern *alt;
+                               alt = rune_altern_new(NULL,
+                                                     parts[j],
+                                                     parts[j+1][0],
+                                                     parts[j+2]);
+                               rune_restr_add_altern(restr, take(alt));
+                       }
+                       rune_add_restr(rune1, take(restr));
+                       ok1(rune_eq(rune1, rune2));
+               } else if (streq(parts[0], "MALFORMED")) {
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_string(parts, parts[2]);
+                       ok1(!rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(!rune2);
+               } else if (streq(parts[0], "BAD DERIVATION")) {
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_string(parts, parts[2]);
+                       ok1(rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(rune2);
+                       ok1(rune_eq(rune1, rune2));
+                       ok1(rune_is_derived(mr, rune1) != NULL);
+                       ok1(rune_is_derived(mr, rune2) != NULL);
+               } else {
+                       const char *err;
+                       diag("test %s", parts[0]);
+                       err = rune_test(parts, mr, rune1, check, parts);
+                       if (streq(parts[0], "PASS")) {
+                               ok1(!err);
+                       } else {
+                               assert(streq(parts[0], "FAIL"));
+                               ok1(err);
+                       }
+               }
+       }
+
+       tal_free(mr);
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/rune/test/test_vectors.csv b/ccan/rune/test/test_vectors.csv
new file mode 100644 (file)
index 0000000..880ea3c
--- /dev/null
@@ -0,0 +1,156 @@
+VALID,empty rune (secret = [0]*16),374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb:,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=
+PASS
+PASS,f1=1
+PASS,f1=var
+PASS,f1=\|\&\\
+VALID,unique id 1,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:=1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,1
+VALID,unique id 2 version 1,4520773407c9658646326fdffe685ffbc3c8639a080dae4310b371830a205cf1:=2-1,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,2,1
+VALID,f1 is missing,64a926b7185d7cf98e10a07dfc4e83d2a826896ebdb112ac964566fa2d50b464:f1!,ZKkmtxhdfPmOEKB9_E6D0qgmiW69sRKslkVm-i1QtGRmMSE=
+PASS
+PASS,f2=f1
+FAIL,f1=1
+FAIL,f1=var
+VALID,f1 equals v1,745c6e39cd41ee9f8388af8ad882bae4ee4e8f6b373f7682cc64d8574551fa5f:f1=v1,dFxuOc1B7p-DiK-K2IK65O5Oj2s3P3aCzGTYV0VR-l9mMT12MQ==
+PASS,f1=v1
+FAIL,f1=v
+FAIL,f1=v1a
+FAIL
+FAIL,f2=f1
+VALID,f1 not equal v1,c9236a6532bfa8e24bec9a66e96af3fb355f817770e79c5a81f6dd0b5ed20e47:f1/v1,ySNqZTK_qOJL7Jpm6Wrz-zVfgXdw55xagfbdC17SDkdmMS92MQ==
+PASS,f1=v2
+PASS,f1=v
+PASS,f1=v1a
+FAIL
+FAIL,f2=v1
+VALID,f1 ends with v1,71f2a1ec9631efc75b01db15fe1f025327ab467f8a83e6bfa7506da222adc5a2:f1$v1,cfKh7JYx78dbAdsV_h8CUyerRn-Kg-a_p1BtoiKtxaJmMSR2MQ==
+PASS,f1=v1
+PASS,f1=2v1
+FAIL,f1=v1a
+FAIL
+VALID,f1 starts with v1,5b13dffbbd9f7b191b0557595d10b22c0acec0c567f8efeba1d7d047927d7bce:f1^v1,WxPf-72fexkbBVdZXRCyLArOwMVn-O_rodfQR5J9e85mMV52MQ==
+PASS,f1=v1
+PASS,f1=v1a
+FAIL,f1=2v1
+FAIL
+VALID,f1 contains v1,ccbe593b72e0ab29446e46796ccd0c775ecd7a327fcc9ddc00fd3910cdacca00:f1~v1,zL5ZO3LgqylEbkZ5bM0Md17NejJ_zJ3cAP05EM2sygBmMX52MQ==
+PASS,f1=v1
+PASS,f1=v1a
+PASS,f1=2v1
+PASS,f1=2v12
+FAIL,f1=1v2
+FAIL
+VALID,f1 less than v1,caff52cedb9241dc00aea7cefc2b89b0a7445b1a4e34c48a5a2b91d2fe76d31f:f1<v1,yv9SztuSQdwArqfO_CuJsKdEWxpONMSKWiuR0v520x9mMTx2MQ==
+FAIL,f1=1
+FAIL,f1=2
+FAIL,f1=v1
+FAIL
+VALID,f1 less than 1,f9776db54fb54c8dd6af20a65a0f210a752a0ee4d1b0a0e7fd9d7ef65af76f84:f1<1,-XdttU-1TI3WryCmWg8hCnUqDuTRsKDn_Z1-9lr3b4RmMTwx
+PASS,f1=0
+PASS,f1=-10000
+FAIL,f1=1
+FAIL,f1=10000
+FAIL,f1=v1
+FAIL
+VALID,f1 greater than v1,2135748f1956d9dfa3c5b09ab6af9d6bb06a41c5bcf93d3f8105cb278af5ac56:f1>v1,ITV0jxlW2d-jxbCatq-da7BqQcW8-T0_gQXLJ4r1rFZmMT52MQ==
+FAIL,f1=1
+FAIL,f1=2
+FAIL,f1=v1
+FAIL
+VALID,f1 greater than 1,84e9991dd941bac97cc681eefec5dd7ac3668a4490ca6b0f19f0e79d2bb9c746:f1>1,hOmZHdlBusl8xoHu_sXdesNmikSQymsPGfDnnSu5x0ZmMT4x
+PASS,f1=2
+PASS,f1=10000
+FAIL,f1=1
+FAIL,f1=-10000
+FAIL,f1=0
+FAIL,f1=v1
+FAIL
+VALID,f1 sorts before 11,b9653ad0dcad7e5ed183f98cdd7e616acd07a98cc66a107a67626290bf000236:f1{11,uWU60Nytfl7Rg_mM3X5has0HqYzGahB6Z2JikL8AAjZmMXsxMQ==
+PASS,f1=0
+PASS,f1=1
+PASS,f1=       
+PASS,f1=/
+FAIL,f1=11
+FAIL,f1=111
+FAIL,f1=v1
+FAIL,f1=:
+FAIL
+VALID,f1 sorts after 11,8c1f6c7c39badc5dea850192a0a4c6e9dd96bf33d410adc5a08fc375b22a1a52:f1}11,jB9sfDm63F3qhQGSoKTG6d2WvzPUEK3FoI_DdbIqGlJmMX0xMQ==
+PASS,f1=111
+PASS,f1=v1
+PASS,f1=:
+FAIL,f1=0
+FAIL,f1=1
+FAIL,f1=       
+FAIL,f1=/
+FAIL,f1=11
+FAIL
+VALID,f1 comment 11,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMQ==
+PASS,f1=111
+PASS,f1=v1
+PASS,f1=:
+PASS,f1=0
+PASS,f1=1
+PASS,f1=       
+PASS,f1=/
+PASS,f1=11
+PASS
+VALID,f_with_underscores equals v1,ee979e1f2c376d69923aab0e8e001111963af038bdce394ffd7ecdc9e7020a6e:f_with_underscores=v1,7peeHyw3bWmSOqsOjgAREZY68Di9zjlP_X7NyecCCm5mX3dpdGhfdW5kZXJzY29yZXM9djE=
+PASS,f_with_underscores=v1
+FAIL,f_with_underscores=v
+FAIL,f_with_underscores=v1a
+FAIL
+FAIL,f2=f_with_underscores
+VALID,f1=1 or f2=3,85c3643dc102f0a0d6f20eeb8c294092151688fae41ef7c8ec7272ab23918376:f1=1|f2=3,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=
+PASS,f1=1
+PASS,f1=1,f2=2
+PASS,f2=3
+PASS,f1=var,f2=3
+PASS,f1=1,f2=3
+FAIL
+FAIL,f1=2
+FAIL,f1=f1
+FAIL,f2=1
+FAIL,f2=f1
+DERIVE,unique_id 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,,=,1
+DERIVE,unique_id 2 version 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,,=,2-1
+DERIVE,f1=1 or f2=3,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,f1,=,1,f2,=,3
+DERIVE,AND f3 contains &|\,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,S253BW1Lragb1CpCSLXYGt9AdrE4iFMlXmnO0alV5vlmMT0xfGYyPTMmZjN-XCZcfFxc,f3,~,&|\
+PASS,f1=1,f3=&|\
+PASS,f2=3,f3=&|\x
+FAIL
+FAIL,f1=1
+FAIL,f2=3
+FAIL,f1=1,f2=3
+FAIL,f1=2,f3=&|\
+FAIL,f2=2,f3=&|\
+FAIL,f3=&|\
+MALFORMED,unique id must use = not !,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:!1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0hMQ==
+MALFORMED,unique id must use = not /,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:/1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0vMQ==
+MALFORMED,unique id must use = not ^,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:^1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1eMQ==
+MALFORMED,unique id must use = not $,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:$1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0kMQ==
+MALFORMED,unique id must use = not ~,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:~1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1-MQ==
+MALFORMED,unique id must use = not <,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:<1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL08MQ==
+MALFORMED,unique id must use = not >,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:>1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0-MQ==
+MALFORMED,unique id must use = not },6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:}1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL19MQ==
+MALFORMED,unique id must use = not {,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:{1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL17MQ==
+MALFORMED,unique id cannot be overridden,7a63a2966d38e6fed89256d4a6e983a6813bf084d4fc6c20b9cdaef24b23fa7e:=1-2&=3,emOilm045v7YklbUpumDpoE78ITU_Gwguc2u8ksj-n49MS0yJj0z
+MALFORMED,version cannot be overridden,db823224f960976b3ee142ce8899fc7ea461b42617e7d16167b1886c5988c628:=1-2&=1-3,24IyJPlgl2s-4ULOiJn8fqRhtCYX59FhZ7GIbFmIxig9MS0yJj0xLTM=
+MALFORMED,Bad condition ",76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1"11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSIxMQ==
+MALFORMED,Bad condition &,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1&11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSYxMQ==
+MALFORMED,Bad condition ',76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1'11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMScxMQ==
+MALFORMED,Bad condition (,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1(11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSgxMQ==
+MALFORMED,Bad condition ),76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1)11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSkxMQ==
+MALFORMED,Bad condition *,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1*11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSoxMQ==
+MALFORMED,Bad condition +,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1+11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSsxMQ==
+MALFORMED,Bad condition -,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1-11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS0xMQ==
+MALFORMED,Bad condition .,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1.11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS4xMQ==
+MALFORMED,Bad condition :,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1:11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMToxMQ==
+MALFORMED,Bad condition ;,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1;11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMTsxMQ==
+MALFORMED,Bad condition ?,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1?11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMT8xMQ==
+MALFORMED,Bad condition [,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1[11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVsxMQ==
+MALFORMED,Bad condition \,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1\11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVwxMQ==
+MALFORMED,Bad condition ],76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1]11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV0xMQ==
+MALFORMED,Bad condition `,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1`11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMWAxMQ==
+MALFORMED,Bad condition |,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1|11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMXwxMQ==
+BAD DERIVATION,Incremented sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0e:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw5mMSMxMQ==
+BAD DERIVATION,Unchanged sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11&a=1,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMSZhPTE=
index 71ca87d582e590ba61745e35d956264d2061d411..6145da3007657319e5c7da615cf862fe4ef10dcb 100644 (file)
@@ -70,7 +70,8 @@ static bool decode_8_chars(const char c[8], beint64_t *res, int *bytes)
 {
        uint64_t acc = 0;
        size_t num_pad = 0;
 {
        uint64_t acc = 0;
        size_t num_pad = 0;
-       for (int i = 0; i < 8; i++) {
+       int i;
+       for (i = 0; i < 8; i++) {
                const char *p;
 
                acc <<= 5;
                const char *p;
 
                acc <<= 5;
index d34de811b3542dbea10610f875f554dc4e734085..8eb4ab42fc812d32f5b0bb99c2b31e82fc0b90b2 100644 (file)
@@ -22,7 +22,8 @@ static int stringbuilder_cpy(
        if (*str != s) {
                if (!s_len)
                        s_len = strlen(s);
        if (*str != s) {
                if (!s_len)
                        s_len = strlen(s);
-               if (s_len > *str_sz)
+               /* Include nul term! */
+               if (s_len >= *str_sz)
                        return EMSGSIZE;
                strcpy(*str, s);
        }
                        return EMSGSIZE;
                strcpy(*str, s);
        }
index 9fa51d0d40db4cf89fb16041585d71ea163b2238..16a30e036ad44c3492a9e05074747b7b15a6bff6 100644 (file)
@@ -17,9 +17,8 @@ struct node {
 };
 
 /* Closest member to this in a non-empty map. */
 };
 
 /* Closest member to this in a non-empty map. */
-static struct strmap *closest(struct strmap *n, const char *member)
+static struct strmap *closest(struct strmap *n, const char *member, size_t len)
 {
 {
-       size_t len = strlen(member);
        const u8 *bytes = (const u8 *)member;
 
        /* Anything with NULL value is a node. */
        const u8 *bytes = (const u8 *)member;
 
        /* Anything with NULL value is a node. */
@@ -35,20 +34,26 @@ static struct strmap *closest(struct strmap *n, const char *member)
        return n;
 }
 
        return n;
 }
 
-void *strmap_get_(const struct strmap *map, const char *member)
+void *strmap_getn_(const struct strmap *map,
+                  const char *member, size_t memberlen)
 {
        struct strmap *n;
 
        /* Not empty map? */
        if (map->u.n) {
 {
        struct strmap *n;
 
        /* Not empty map? */
        if (map->u.n) {
-               n = closest((struct strmap *)map, member);
-               if (streq(member, n->u.s))
+               n = closest((struct strmap *)map, member, memberlen);
+               if (!strncmp(member, n->u.s, memberlen) && !n->u.s[memberlen])
                        return n->v;
        }
        errno = ENOENT;
        return NULL;
 }
 
                        return n->v;
        }
        errno = ENOENT;
        return NULL;
 }
 
+void *strmap_get_(const struct strmap *map, const char *member)
+{
+       return strmap_getn_(map, member, strlen(member));
+}
+
 bool strmap_add_(struct strmap *map, const char *member, const void *value)
 {
        size_t len = strlen(member);
 bool strmap_add_(struct strmap *map, const char *member, const void *value)
 {
        size_t len = strlen(member);
@@ -68,7 +73,7 @@ bool strmap_add_(struct strmap *map, const char *member, const void *value)
        }
 
        /* Find closest existing member. */
        }
 
        /* Find closest existing member. */
-       n = closest(map, member);
+       n = closest(map, member, len);
 
        /* Find where they differ. */
        for (byte_num = 0; n->u.s[byte_num] == member[byte_num]; byte_num++) {
 
        /* Find where they differ. */
        for (byte_num = 0; n->u.s[byte_num] == member[byte_num]; byte_num++) {
index 0b32e6befc0ca2974ad7304dd967e1c95fae75d3..8724c31dcbb20d2686cd33cbc39149209af7b767 100644 (file)
@@ -72,7 +72,7 @@ static inline bool strmap_empty_(const struct strmap *map)
 /**
  * strmap_get - get a value from a string map
  * @map: the typed strmap to search.
 /**
  * strmap_get - get a value from a string map
  * @map: the typed strmap to search.
- * @member: the string to search for.
+ * @member: the string to search for (nul terminated)
  *
  * Returns the value, or NULL if it isn't in the map (and sets errno = ENOENT).
  *
  *
  * Returns the value, or NULL if it isn't in the map (and sets errno = ENOENT).
  *
@@ -85,6 +85,23 @@ static inline bool strmap_empty_(const struct strmap *map)
        tcon_cast((map), canary, strmap_get_(tcon_unwrap(map), (member)))
 void *strmap_get_(const struct strmap *map, const char *member);
 
        tcon_cast((map), canary, strmap_get_(tcon_unwrap(map), (member)))
 void *strmap_get_(const struct strmap *map, const char *member);
 
+/**
+ * strmap_getn - get a value from a string map
+ * @map: the typed strmap to search.
+ * @member: the string to search for.
+ * @memberlen: the length of @member.
+ *
+ * Returns the value, or NULL if it isn't in the map (and sets errno = ENOENT).
+ *
+ * Example:
+ *     val = strmap_getn(&map, "hello", 5);
+ *     if (val)
+ *             printf("hello => %i\n", *val);
+ */
+#define strmap_getn(map, member, n)                                    \
+       tcon_cast((map), canary, strmap_getn_(tcon_unwrap(map), (member), (n)))
+void *strmap_getn_(const struct strmap *map, const char *member, size_t n);
+
 /**
  * strmap_add - place a member in the string map.
  * @map: the typed strmap to add to.
 /**
  * strmap_add - place a member in the string map.
  * @map: the typed strmap to add to.
index a49541114ff863128dd03a1429fa84c1d4e06ffb..1230d8cacafcd2bfb05b659435480ba0ac77f9c5 100644 (file)
@@ -28,7 +28,8 @@ enum prop_type {
 
 struct tal_hdr {
        struct list_node list;
 
 struct tal_hdr {
        struct list_node list;
-       struct prop_hdr *prop;
+       /* Use is_prop_hdr tell if this is a struct prop_hdr or string! */
+       char *prop;
        /* XOR with TAL_PTR_OBFUSTICATOR */
        intptr_t parent_child;
        size_t bytelen;
        /* XOR with TAL_PTR_OBFUSTICATOR */
        intptr_t parent_child;
        size_t bytelen;
@@ -36,7 +37,8 @@ struct tal_hdr {
 
 struct prop_hdr {
        enum prop_type type;
 
 struct prop_hdr {
        enum prop_type type;
-       struct prop_hdr *next;
+       /* Use is_prop_hdr to tell if this is a struct prop_hdr or string! */
+       char *next;
 };
 
 struct children {
 };
 
 struct children {
@@ -72,7 +74,7 @@ static struct {
        struct tal_hdr hdr;
        struct children c;
 } null_parent = { { { &null_parent.hdr.list, &null_parent.hdr.list },
        struct tal_hdr hdr;
        struct children c;
 } null_parent = { { { &null_parent.hdr.list, &null_parent.hdr.list },
-                   &null_parent.c.hdr, TAL_PTR_OBFUSTICATOR, 0 },
+               (char *)&null_parent.c.hdr, TAL_PTR_OBFUSTICATOR, 0 },
                  { { CHILDREN, NULL },
                    &null_parent.hdr,
                    { { &null_parent.c.children.n,
                  { { CHILDREN, NULL },
                    &null_parent.hdr,
                    { { &null_parent.c.children.n,
@@ -123,9 +125,11 @@ void tal_cleanup(void)
 }
 
 /* We carefully start all real properties with a zero byte. */
 }
 
 /* We carefully start all real properties with a zero byte. */
-static bool is_literal(const struct prop_hdr *prop)
+static struct prop_hdr *is_prop_hdr(const char *ptr)
 {
 {
-       return ((char *)prop)[0] != 0;
+       if (*ptr != 0)
+               return NULL;
+       return (struct prop_hdr *)ptr;
 }
 
 #ifndef NDEBUG
 }
 
 #ifndef NDEBUG
@@ -174,8 +178,11 @@ static struct tal_hdr *to_tal_hdr(const void *ctx)
        check_bounds(ignore_destroying_bit(t->parent_child));
        check_bounds(t->list.next);
        check_bounds(t->list.prev);
        check_bounds(ignore_destroying_bit(t->parent_child));
        check_bounds(t->list.next);
        check_bounds(t->list.prev);
-       if (t->prop && !is_literal(t->prop))
-               check_bounds(t->prop);
+       if (t->prop) {
+               struct prop_hdr *p = is_prop_hdr(t->prop);
+               if (p)
+                       check_bounds(p);
+       }
        return t;
 }
 
        return t;
 }
 
@@ -215,13 +222,12 @@ static void notify(const struct tal_hdr *ctx,
                   enum tal_notify_type type, const void *info,
                   int saved_errno)
 {
                   enum tal_notify_type type, const void *info,
                   int saved_errno)
 {
-        const struct prop_hdr *p;
+        const char *ptr;
+       const struct prop_hdr *p;
 
 
-        for (p = ctx->prop; p; p = p->next) {
+        for (ptr = ctx->prop; ptr && (p = is_prop_hdr(ptr)) != NULL; ptr = p->next) {
                struct notifier *n;
 
                struct notifier *n;
 
-                if (is_literal(p))
-                       break;
                 if (p->type != NOTIFIER)
                        continue;
                n = (struct notifier *)p;
                 if (p->type != NOTIFIER)
                        continue;
                n = (struct notifier *)p;
@@ -255,29 +261,54 @@ static void *allocate(size_t size)
        return ret;
 }
 
        return ret;
 }
 
-static struct prop_hdr **find_property_ptr(const struct tal_hdr *t,
-                                          enum prop_type type)
+/* Returns a pointer to the pointer: can cast (*ret) to a (struct prop_ptr *) */
+static char **find_property_ptr(struct tal_hdr *t, enum prop_type type)
 {
 {
-        struct prop_hdr **p;
+       char **ptr;
+        struct prop_hdr *p;
 
 
-        for (p = (struct prop_hdr **)&t->prop; *p; p = &(*p)->next) {
-                if (is_literal(*p)) {
-                        if (type == NAME)
-                                return p;
-                        break;
-                }
-                if ((*p)->type == type)
-                        return p;
-        }
-        return NULL;
+       /* NAME is special, as it can be a literal: see find_name_property */
+       assert(type != NAME);
+       for (ptr = &t->prop; *ptr; ptr = &p->next) {
+               if (!is_prop_hdr(*ptr))
+                       break;
+               p = (struct prop_hdr *)*ptr;
+                if (p->type == type)
+                        return ptr;
+       }
+       return NULL;
+}
+
+/* This is special:
+ * NULL - not found
+ * *literal: true - char **, pointer to literal pointer.
+ * *literal: false - struct prop_hdr **, pointer to header ptr.
+ */
+static char **find_name_property(struct tal_hdr *t, bool *literal)
+{
+       char **ptr;
+        struct prop_hdr *p;
+
+       for (ptr = &t->prop; *ptr; ptr = &p->next) {
+               if (!is_prop_hdr(*ptr)) {
+                       *literal = true;
+                       return ptr;
+               }
+               p = (struct prop_hdr *)*ptr;
+                if (p->type == NAME) {
+                       *literal = false;
+                        return ptr;
+               }
+       }
+       return NULL;
 }
 
 }
 
-static void *find_property(const struct tal_hdr *parent, enum prop_type type)
+static void *find_property(struct tal_hdr *parent, enum prop_type type)
 {
 {
-        struct prop_hdr **p = find_property_ptr(parent, type);
+        char **ptr = find_property_ptr(parent, type);
 
 
-        if (p)
-                return *p;
+        if (ptr)
+                return (struct prop_hdr *)*ptr;
         return NULL;
 }
 
         return NULL;
 }
 
@@ -287,7 +318,7 @@ static void init_property(struct prop_hdr *hdr,
 {
        hdr->type = type;
        hdr->next = parent->prop;
 {
        hdr->type = type;
        hdr->next = parent->prop;
-       parent->prop = hdr;
+       parent->prop = (char *)hdr;
 }
 
 static struct notifier *add_notifier_property(struct tal_hdr *t,
 }
 
 static struct notifier *add_notifier_property(struct tal_hdr *t,
@@ -321,17 +352,20 @@ static enum tal_notify_type del_notifier_property(struct tal_hdr *t,
                                                  bool match_extra_arg,
                                                  void *extra_arg)
 {
                                                  bool match_extra_arg,
                                                  void *extra_arg)
 {
-        struct prop_hdr **p;
+       char **ptr;
+       struct prop_hdr *p;
 
 
-        for (p = (struct prop_hdr **)&t->prop; *p; p = &(*p)->next) {
+       for (ptr = &t->prop; *ptr; ptr = &p->next) {
                struct notifier *n;
                enum tal_notify_type types;
 
                struct notifier *n;
                enum tal_notify_type types;
 
-                if (is_literal(*p))
+               p = is_prop_hdr(*ptr);
+               if (!p)
                        break;
                        break;
-                if ((*p)->type != NOTIFIER)
+
+                if (p->type != NOTIFIER)
                        continue;
                        continue;
-               n = (struct notifier *)*p;
+               n = (struct notifier *)p;
                if (n->u.notifyfn != fn)
                        continue;
 
                if (n->u.notifyfn != fn)
                        continue;
 
@@ -341,8 +375,8 @@ static enum tal_notify_type del_notifier_property(struct tal_hdr *t,
                    && extra_arg != EXTRA_ARG(n))
                        continue;
 
                    && extra_arg != EXTRA_ARG(n))
                        continue;
 
-               *p = (*p)->next;
-               freefn(n);
+               *ptr = p->next;
+               freefn(p);
                return types & ~(NOTIFY_IS_DESTRUCTOR|NOTIFY_EXTRA_ARG);
         }
         return 0;
                return types & ~(NOTIFY_IS_DESTRUCTOR|NOTIFY_EXTRA_ARG);
         }
         return 0;
@@ -388,7 +422,8 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child)
 
 static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
 {
 
 static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
 {
-       struct prop_hdr **prop, *p, *next;
+       struct prop_hdr *prop;
+       char *ptr, *next;
 
        assert(!taken(from_tal_hdr(t)));
 
 
        assert(!taken(from_tal_hdr(t)));
 
@@ -402,10 +437,10 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
        notify(t, TAL_NOTIFY_FREE, (tal_t *)orig, saved_errno);
 
        /* Now free children and groups. */
        notify(t, TAL_NOTIFY_FREE, (tal_t *)orig, saved_errno);
 
        /* Now free children and groups. */
-       prop = find_property_ptr(t, CHILDREN);
+       prop = find_property(t, CHILDREN);
        if (prop) {
                struct tal_hdr *i;
        if (prop) {
                struct tal_hdr *i;
-               struct children *c = (struct children *)*prop;
+               struct children *c = (struct children *)prop;
 
                while ((i = list_top(&c->children, struct tal_hdr, list))) {
                        list_del(&i->list);
 
                while ((i = list_top(&c->children, struct tal_hdr, list))) {
                        list_del(&i->list);
@@ -414,9 +449,9 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
        }
 
         /* Finally free our properties. */
        }
 
         /* Finally free our properties. */
-        for (p = t->prop; p && !is_literal(p); p = next) {
-                next = p->next;
-               freefn(p);
+       for (ptr = t->prop; ptr && (prop = is_prop_hdr(ptr)); ptr = next) {
+                next = prop->next;
+               freefn(ptr);
         }
         freefn(t);
 }
         }
         freefn(t);
 }
@@ -590,25 +625,34 @@ bool tal_del_destructor2_(const tal_t *ctx, void (*destroy)(void *me, void *arg)
 bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
 {
         struct tal_hdr *t = debug_tal(to_tal_hdr(ctx));
 bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
 {
         struct tal_hdr *t = debug_tal(to_tal_hdr(ctx));
-        struct prop_hdr **prop = find_property_ptr(t, NAME);
+       bool was_literal;
+       char **nptr;
 
         /* Get rid of any old name */
 
         /* Get rid of any old name */
-        if (prop) {
-                struct name *name = (struct name *)*prop;
-                if (is_literal(&name->hdr))
-                        *prop = NULL;
-                else {
-                        *prop = name->hdr.next;
-                       freefn(name);
-                }
+       nptr = find_name_property(t, &was_literal);
+       if (nptr) {
+               if (was_literal)
+                       *nptr = NULL;
+               else {
+                       struct name *oldname;
+
+                       oldname = (struct name *)*nptr;
+                       *nptr = oldname->hdr.next;
+                       freefn(oldname);
+               }
         }
 
         if (literal && name[0]) {
         }
 
         if (literal && name[0]) {
-                struct prop_hdr **p;
+               char **ptr;
+               struct prop_hdr *prop;
 
                 /* Append literal. */
 
                 /* Append literal. */
-                for (p = &t->prop; *p && !is_literal(*p); p = &(*p)->next);
-                *p = (struct prop_hdr *)name;
+               for (ptr = &t->prop; *ptr; ptr = &prop->next) {
+                       prop = is_prop_hdr(*ptr);
+                       if (!prop)
+                               break;
+               }
+                *ptr = (char *)name;
         } else if (!add_name_property(t, name))
                return false;
 
         } else if (!add_name_property(t, name))
                return false;
 
@@ -620,15 +664,16 @@ bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
 
 const char *tal_name(const tal_t *t)
 {
 
 const char *tal_name(const tal_t *t)
 {
-        struct name *n;
+       char **nptr;
+       bool literal;
 
 
-       n = find_property(debug_tal(to_tal_hdr(t)), NAME);
-       if (!n)
+       nptr = find_name_property(debug_tal(to_tal_hdr(t)), &literal);
+       if (!nptr)
                return NULL;
                return NULL;
+       if (literal)
+               return *nptr;
 
 
-       if (is_literal(&n->hdr))
-               return (const char *)n;
-       return n->name;
+       return ((struct name *)(*nptr))->name;
 }
 
 size_t tal_bytelen(const tal_t *ptr)
 }
 
 size_t tal_bytelen(const tal_t *ptr)
@@ -767,11 +812,17 @@ out:
 }
 
 void *tal_dup_(const tal_t *ctx, const void *p, size_t size,
 }
 
 void *tal_dup_(const tal_t *ctx, const void *p, size_t size,
-              size_t n, size_t extra, const char *label)
+              size_t n, size_t extra, bool nullok, const char *label)
 {
        void *ret;
        size_t nbytes = size;
 
 {
        void *ret;
        size_t nbytes = size;
 
+       if (nullok && p == NULL) {
+               /* take(NULL) works. */
+               (void)taken(p);
+               return NULL;
+       }
+       
        if (!adjust_size(&nbytes, n)) {
                if (taken(p))
                        tal_free(p);
        if (!adjust_size(&nbytes, n)) {
                if (taken(p))
                        tal_free(p);
@@ -797,11 +848,16 @@ void *tal_dup_(const tal_t *ctx, const void *p, size_t size,
        }
 
        ret = tal_alloc_arr_(ctx, size, n + extra, false, label);
        }
 
        ret = tal_alloc_arr_(ctx, size, n + extra, false, label);
-       if (ret)
+       if (ret && p)
                memcpy(ret, p, nbytes);
        return ret;
 }
 
                memcpy(ret, p, nbytes);
        return ret;
 }
 
+void *tal_dup_talarr_(const tal_t *ctx, const tal_t *src TAKES, const char *label)
+{
+       return tal_dup_(ctx, src, 1, tal_bytelen(src), 0, true, label);
+}
+
 void tal_set_backend(void *(*alloc_fn)(size_t size),
                     void *(*resize_fn)(void *, size_t size),
                     void (*free_fn)(void *),
 void tal_set_backend(void *(*alloc_fn)(size_t size),
                     void *(*resize_fn)(void *, size_t size),
                     void (*free_fn)(void *),
@@ -821,39 +877,41 @@ void tal_set_backend(void *(*alloc_fn)(size_t size),
 static void dump_node(unsigned int indent, const struct tal_hdr *t)
 {
        unsigned int i;
 static void dump_node(unsigned int indent, const struct tal_hdr *t)
 {
        unsigned int i;
-        const struct prop_hdr *p;
+        const struct prop_hdr *prop;
+       const char *ptr;
 
        for (i = 0; i < indent; i++)
 
        for (i = 0; i < indent; i++)
-               printf("  ");
-       printf("%p len=%zu", t, t->bytelen);
-        for (p = t->prop; p; p = p->next) {
+               fprintf(stderr, "  ");
+       fprintf(stderr, "%p len=%zu", t, t->bytelen);
+        for (ptr = t->prop; ptr; ptr = prop->next) {
                struct children *c;
                struct name *n;
                struct notifier *no;
                struct children *c;
                struct name *n;
                struct notifier *no;
-                if (is_literal(p)) {
-                       printf(" \"%s\"", (const char *)p);
+                prop = is_prop_hdr(ptr);
+               if (!prop) {
+                       fprintf(stderr, " \"%s\"", ptr);
                        break;
                }
                        break;
                }
-               switch (p->type) {
+               switch (prop->type) {
                case CHILDREN:
                case CHILDREN:
-                       c = (struct children *)p;
-                       printf(" CHILDREN(%p):parent=%p,children={%p,%p}\n",
-                              p, c->parent,
+                       c = (struct children *)prop;
+                       fprintf(stderr, " CHILDREN(%p):parent=%p,children={%p,%p}",
+                              prop, c->parent,
                               c->children.n.prev, c->children.n.next);
                        break;
                case NAME:
                               c->children.n.prev, c->children.n.next);
                        break;
                case NAME:
-                       n = (struct name *)p;
-                       printf(" NAME(%p):%s", p, n->name);
+                       n = (struct name *)prop;
+                       fprintf(stderr, " NAME(%p):%s", prop, n->name);
                        break;
                case NOTIFIER:
                        break;
                case NOTIFIER:
-                       no = (struct notifier *)p;
-                       printf(" NOTIFIER(%p):fn=%p", p, no->u.notifyfn);
+                       no = (struct notifier *)prop;
+                       fprintf(stderr, " NOTIFIER(%p):fn=%p", prop, no->u.notifyfn);
                        break;
                default:
                        break;
                default:
-                       printf(" **UNKNOWN(%p):%i**", p, p->type);
+                       fprintf(stderr, " **UNKNOWN(%p):%i**", prop, prop->type);
                }
        }
                }
        }
-       printf("\n");
+       fprintf(stderr, "\n");
 }
 
 static void tal_dump_(unsigned int level, const struct tal_hdr *t)
 }
 
 static void tal_dump_(unsigned int level, const struct tal_hdr *t)
@@ -862,7 +920,7 @@ static void tal_dump_(unsigned int level, const struct tal_hdr *t)
 
        dump_node(level, t);
 
 
        dump_node(level, t);
 
-       children = find_property(t, CHILDREN);
+       children = find_property((struct tal_hdr *)t, CHILDREN);
        if (children) {
                struct tal_hdr *i;
 
        if (children) {
                struct tal_hdr *i;
 
@@ -893,7 +951,8 @@ static bool check_err(struct tal_hdr *t, const char *errorstr,
 static bool check_node(struct children *parent_child,
                       struct tal_hdr *t, const char *errorstr)
 {
 static bool check_node(struct children *parent_child,
                       struct tal_hdr *t, const char *errorstr)
 {
-       struct prop_hdr *p;
+       struct prop_hdr *prop;
+       char *p;
        struct name *name = NULL;
        struct children *children = NULL;
 
        struct name *name = NULL;
        struct children *children = NULL;
 
@@ -903,23 +962,24 @@ static bool check_node(struct children *parent_child,
        if (ignore_destroying_bit(t->parent_child) != parent_child)
                return check_err(t, errorstr, "incorrect parent");
 
        if (ignore_destroying_bit(t->parent_child) != parent_child)
                return check_err(t, errorstr, "incorrect parent");
 
-       for (p = t->prop; p; p = p->next) {
-               if (is_literal(p)) {
+       for (p = t->prop; p; p = prop->next) {
+               prop = is_prop_hdr(p);
+               if (!prop) {
                        if (name)
                                return check_err(t, errorstr,
                                                 "has extra literal");
                        break;
                }
                        if (name)
                                return check_err(t, errorstr,
                                                 "has extra literal");
                        break;
                }
-               if (!in_bounds(p))
+               if (!in_bounds(prop))
                        return check_err(t, errorstr,
                                         "has bad property pointer");
 
                        return check_err(t, errorstr,
                                         "has bad property pointer");
 
-               switch (p->type) {
+               switch (prop->type) {
                case CHILDREN:
                        if (children)
                                return check_err(t, errorstr,
                                                 "has two child nodes");
                case CHILDREN:
                        if (children)
                                return check_err(t, errorstr,
                                                 "has two child nodes");
-                       children = (struct children *)p;
+                       children = (struct children *)prop;
                        break;
                case NOTIFIER:
                        break;
                        break;
                case NOTIFIER:
                        break;
@@ -927,7 +987,7 @@ static bool check_node(struct children *parent_child,
                        if (name)
                                return check_err(t, errorstr,
                                                 "has two names");
                        if (name)
                                return check_err(t, errorstr,
                                                 "has two names");
-                       name = (struct name *)p;
+                       name = (struct name *)prop;
                        break;
                default:
                        return check_err(t, errorstr, "has unknown property");
                        break;
                default:
                        return check_err(t, errorstr, "has unknown property");
index 8b7ffca5ea8125ef8ba36a623d989e8aad6fff33..c486f9e8e194a99b51ffdb8117788bbae86a89ed 100644 (file)
@@ -131,10 +131,11 @@ void *tal_free(const tal_t *p);
 /**
  * tal_steal - change the parent of a tal-allocated pointer.
  * @ctx: The new parent.
 /**
  * tal_steal - change the parent of a tal-allocated pointer.
  * @ctx: The new parent.
- * @ptr: The tal allocated object to move.
+ * @ptr: The tal allocated object to move, or NULL.
  *
  * This may need to perform an allocation, in which case it may fail; thus
  *
  * This may need to perform an allocation, in which case it may fail; thus
- * it can return NULL, otherwise returns @ptr.
+ * it can return NULL, otherwise returns @ptr.  If @ptr is NULL, this function does
+ * nothing.
  */
 #if HAVE_STATEMENT_EXPR
 /* Weird macro avoids gcc's 'warning: value computed is not used'. */
  */
 #if HAVE_STATEMENT_EXPR
 /* Weird macro avoids gcc's 'warning: value computed is not used'. */
@@ -351,10 +352,21 @@ tal_t *tal_parent(const tal_t *ctx);
  * tal_dup - duplicate an object.
  * @ctx: The tal allocated object to be parent of the result (may be NULL).
  * @type: the type (should match type of @p!)
  * tal_dup - duplicate an object.
  * @ctx: The tal allocated object to be parent of the result (may be NULL).
  * @type: the type (should match type of @p!)
- * @p: the object to copy (or reparented if take())
+ * @p: the object to copy (or reparented if take()).  Must not be NULL.
  */
 #define tal_dup(ctx, type, p)                                  \
  */
 #define tal_dup(ctx, type, p)                                  \
-       tal_dup_label(ctx, type, p, TAL_LABEL(type, ""))
+       tal_dup_label(ctx, type, p, TAL_LABEL(type, ""), false)
+
+/**
+ * tal_dup_or_null - duplicate an object, or just pass NULL.
+ * @ctx: The tal allocated object to be parent of the result (may be NULL).
+ * @type: the type (should match type of @p!)
+ * @p: the object to copy (or reparented if take())
+ *
+ * if @p is NULL, just return NULL, otherwise to tal_dup().
+ */
+#define tal_dup_or_null(ctx, type, p)                                  \
+       tal_dup_label(ctx, type, p, TAL_LABEL(type, ""), true)
 
 /**
  * tal_dup_arr - duplicate an array.
 
 /**
  * tal_dup_arr - duplicate an array.
@@ -368,7 +380,17 @@ tal_t *tal_parent(const tal_t *ctx);
        tal_dup_arr_label(ctx, type, p, n, extra, TAL_LABEL(type, "[]"))
 
 
        tal_dup_arr_label(ctx, type, p, n, extra, TAL_LABEL(type, "[]"))
 
 
-
+/**
+ * tal_dup_arr - duplicate a tal array.
+ * @ctx: The tal allocated object to be parent of the result (may be NULL).
+ * @type: the type (should match type of @p!)
+ * @p: the tal array to copy (or resized & reparented if take())
+ *
+ * The comon case of duplicating an entire tal array.
+ */
+#define tal_dup_talarr(ctx, type, p)                                   \
+       ((type *)tal_dup_talarr_((ctx), tal_typechk_(p, type *),        \
+                                TAL_LABEL(type, "[]")))
 /* Lower-level interfaces, where you want to supply your own label string. */
 #define tal_label(ctx, type, label)                                            \
        ((type *)tal_alloc_((ctx), sizeof(type), false, label))
 /* Lower-level interfaces, where you want to supply your own label string. */
 #define tal_label(ctx, type, label)                                            \
        ((type *)tal_alloc_((ctx), sizeof(type), false, label))
@@ -378,13 +400,13 @@ tal_t *tal_parent(const tal_t *ctx);
        ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), false, label))
 #define tal_arrz_label(ctx, type, count, label)                                        \
        ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), true, label))
        ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), false, label))
 #define tal_arrz_label(ctx, type, count, label)                                        \
        ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), true, label))
-#define tal_dup_label(ctx, type, p, label)                     \
+#define tal_dup_label(ctx, type, p, label, nullok)                     \
        ((type *)tal_dup_((ctx), tal_typechk_(p, type *),       \
        ((type *)tal_dup_((ctx), tal_typechk_(p, type *),       \
-                         sizeof(type), 1, 0,                   \
+                         sizeof(type), 1, 0, nullok,           \
                          label))
 #define tal_dup_arr_label(ctx, type, p, n, extra, label)       \
        ((type *)tal_dup_((ctx), tal_typechk_(p, type *),       \
                          label))
 #define tal_dup_arr_label(ctx, type, p, n, extra, label)       \
        ((type *)tal_dup_((ctx), tal_typechk_(p, type *),       \
-                         sizeof(type), (n), (extra),           \
+                         sizeof(type), (n), (extra), false,    \
                          label))
 
 /**
                          label))
 
 /**
@@ -455,7 +477,7 @@ bool tal_check(const tal_t *ctx, const char *errorstr);
 
 #ifdef CCAN_TAL_DEBUG
 /**
 
 #ifdef CCAN_TAL_DEBUG
 /**
- * tal_dump - dump entire tal tree.
+ * tal_dump - dump entire tal tree to stderr.
  *
  * This is a helper for debugging tal itself, which dumps all the tal internal
  * state.
  *
  * This is a helper for debugging tal itself, which dumps all the tal internal
  * state.
@@ -504,7 +526,9 @@ void *tal_alloc_arr_(const tal_t *ctx, size_t bytes, size_t count, bool clear,
                     const char *label);
 
 void *tal_dup_(const tal_t *ctx, const void *p TAKES, size_t size,
                     const char *label);
 
 void *tal_dup_(const tal_t *ctx, const void *p TAKES, size_t size,
-              size_t n, size_t extra, const char *label);
+              size_t n, size_t extra, bool nullok, const char *label);
+void *tal_dup_talarr_(const tal_t *ctx, const tal_t *src TAKES,
+                     const char *label);
 
 tal_t *tal_steal_(const tal_t *new_parent, const tal_t *t);
 
 
 tal_t *tal_steal_(const tal_t *new_parent, const tal_t *t);
 
index 150f00adae9e2d60c0df655ff2ac105e34c2c85a..47e436408cbe4ce803951eabb59410e2d6fde1d2 100644 (file)
@@ -13,8 +13,8 @@ static void *my_realloc(void *old, size_t size)
        void *new = realloc(old, size);
        if (new == old) {
                void *p = malloc(size);
        void *new = realloc(old, size);
        if (new == old) {
                void *p = malloc(size);
-               memcpy(p, old, size);
-               free(old);
+               memcpy(p, new, size);
+               free(new);
                new = p;
        }
        return new;
                new = p;
        }
        return new;
index 29f055588015f5e38f02513c6dcce39b4657ff20..5cc3352a4a90df99362ac74ea0e9a6635ccafec4 100644 (file)
@@ -56,7 +56,7 @@ static unsigned bucket_of(ssize_t min, unsigned step_bits, ssize_t val)
                return 0;
        }
        assert(step_bits < SIZET_BITS);
                return 0;
        }
        assert(step_bits < SIZET_BITS);
-       return (size_t)(val - min) >> step_bits;
+       return ((size_t)val - (size_t)min) >> step_bits;
 }
 
 /* Return the min value in bucket b. */
 }
 
 /* Return the min value in bucket b. */
@@ -67,7 +67,7 @@ static ssize_t bucket_min(ssize_t min, unsigned step_bits, unsigned b)
                return min;
        }
        assert(step_bits < SIZET_BITS);
                return min;
        }
        assert(step_bits < SIZET_BITS);
-       return min + ((ssize_t)b << step_bits);
+       return min + ((size_t)b << step_bits);
 }
 
 /* Does shifting by this many bits truncate the number? */
 }
 
 /* Does shifting by this many bits truncate the number? */
@@ -76,6 +76,9 @@ static bool shift_overflows(size_t num, unsigned bits)
        if (bits == 0) {
                return false;
        }
        if (bits == 0) {
                return false;
        }
+       if (bits >= SIZET_BITS) {
+               return true;
+       }
 
        return ((num << bits) >> 1) != (num << (bits - 1));
 }
 
        return ((num << bits) >> 1) != (num << (bits - 1));
 }
@@ -94,7 +97,7 @@ static void renormalize(struct tally *tally,
 
        /* If we don't have sufficient range, increase step bits until
         * buckets cover entire range of ssize_t anyway. */
 
        /* If we don't have sufficient range, increase step bits until
         * buckets cover entire range of ssize_t anyway. */
-       range = (new_max - new_min) + 1;
+       range = ((size_t)new_max - (size_t)new_min) + 1;
        while (!shift_overflows(tally->buckets, tally->step_bits)
               && range > ((size_t)tally->buckets << tally->step_bits)) {
                /* Collapse down. */
        while (!shift_overflows(tally->buckets, tally->step_bits)
               && range > ((size_t)tally->buckets << tally->step_bits)) {
                /* Collapse down. */
@@ -113,11 +116,13 @@ static void renormalize(struct tally *tally,
        memset(tally->counts, 0, sizeof(tally->counts[0]) * old_min);
 
        /* If we moved boundaries, adjust buckets to that ratio. */
        memset(tally->counts, 0, sizeof(tally->counts[0]) * old_min);
 
        /* If we moved boundaries, adjust buckets to that ratio. */
-       spill = (tally->min - new_min) % (1 << tally->step_bits);
-       for (i = 0; i < tally->buckets-1; i++) {
-               size_t adjust = (tally->counts[i] >> tally->step_bits) * spill;
-               tally->counts[i] -= adjust;
-               tally->counts[i+1] += adjust;
+       if (tally->step_bits < SIZET_BITS) {
+               spill = (tally->min - new_min) % ((size_t)1 << tally->step_bits);
+               for (i = 0; i < tally->buckets-1; i++) {
+                       size_t adjust = (tally->counts[i] >> tally->step_bits) * spill;
+                       tally->counts[i] -= adjust;
+                       tally->counts[i+1] += adjust;
+               }
        }
 
 update:
        }
 
 update:
index bf8a276c7926a3cad99618cce1eaafbaf9d61582..b3782104fb2b63274f2a97f0f94fac87cb1b9db3 100644 (file)
@@ -43,6 +43,7 @@ static const char *todo_msg_fixed = "libtap malloc issue";
 static int todo = 0;
 static int test_died = 0;
 static int test_pid;
 static int todo = 0;
 static int test_died = 0;
 static int test_pid;
+void (*tap_fail_callback)(void) = NULL;
 
 /* Encapsulate the pthread code in a conditional.  In the absence of
    libpthread the code does nothing.
 
 /* Encapsulate the pthread code in a conditional.  In the absence of
    libpthread the code does nothing.
index 5b21ff756a89a9dd64c2caf61f2899fe65e5e272..22c245d53705ff7f48b273e568984dc5bf3626f2 100644 (file)
@@ -246,6 +246,6 @@ void plan_skip_all(const char *reason);
  *
  * This can be used to ease debugging, or exit on the first failure.
  */
  *
  * This can be used to ease debugging, or exit on the first failure.
  */
-void (*tap_fail_callback)(void);
+extern void (*tap_fail_callback)(void);
 
 #endif /* CCAN_TAP_H */
 
 #endif /* CCAN_TAP_H */
index e0f84b383976c4d106e6d9fe3ee9043c449d1ed8..df3aac88b785a4acc241e125a9c377766a55272d 100644 (file)
  * It evaluates to @x so you can chain it.
  */
 #define tcon_check_ptr(x, canary, expr)                                \
  * It evaluates to @x so you can chain it.
  */
 #define tcon_check_ptr(x, canary, expr)                                \
-       (sizeof(&(x)->_tcon[0].canary == (expr)) ? (x) : (x))
-
+       (sizeof((expr) ? (expr) : &(x)->_tcon[0].canary) ? (x) : (x))
 
 /**
  * tcon_type - the type within a container (or void *)
 
 /**
  * tcon_type - the type within a container (or void *)
index 44645a7ec6d417875ceed8910915efea3a0d2193..ed1d3e206acd97eee8cf46b69e8018a5a87096e4 100644 (file)
@@ -25,7 +25,7 @@ struct info_tcon {
 int main(void)
 {
        struct info_tcon info;
 int main(void)
 {
        struct info_tcon info;
-       struct outer ovar;
+       struct outer ovar = { 0, { 0 } };
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
index 19ba5bdcc915d22851009a630b4bda32dd85f242..a03f6514e1793b0dce08c45cc23cd34c7078d01c 100644 (file)
@@ -21,7 +21,7 @@ int main(void)
 {
        TCON_WRAP(struct info_base,
                  TCON_CONTAINER(concan, struct outer, inner)) info;
 {
        TCON_WRAP(struct info_base,
                  TCON_CONTAINER(concan, struct outer, inner)) info;
-       struct outer ovar;
+       struct outer ovar = { 0, { 0 } };
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
index 9185225a9361903e25e86ce616e31da0e502e577..dfdfdba9a3412ef692311762b9cdfdd54d01c4e5 100644 (file)
@@ -25,7 +25,7 @@ struct info_tcon {
 int main(void)
 {
        struct info_tcon info;
 int main(void)
 {
        struct info_tcon info;
-       struct outer ovar;
+       struct outer ovar = { 0, { 0 } };
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
index 958e5c8b3dca47c67c1f23aa25fe9fc29e759e90..a56e510f1e2bb86f326b917ad61bc17c839281b2 100644 (file)
@@ -21,7 +21,7 @@ int main(void)
 {
        TCON_WRAP(struct info_base,
                  TCON_CONTAINER(concan, struct outer, inner)) info;
 {
        TCON_WRAP(struct info_base,
                  TCON_CONTAINER(concan, struct outer, inner)) info;
-       struct outer ovar;
+       struct outer ovar = { 0, { 0 } };
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
 #ifdef FAIL
 #if !HAVE_TYPEOF
 #error We cannot detect type problems without HAVE_TYPEOF
index 48bded19656456a500703d77d509ba750053bbda..ef6b27742679f4cbccc006d1db81d4cc83ec09fa 100644 (file)
@@ -108,13 +108,10 @@ void timer_addrel(struct timers *timers, struct timer *t, struct timerel rel)
 
        t->time = time_to_grains(timemono_add(time_mono(), rel));
 
 
        t->time = time_to_grains(timemono_add(time_mono(), rel));
 
-#if TIME_HAVE_MONOTONIC
-       assert(t->time >= timers->base);
-#else
        /* Added in the past?  Treat it as imminent. */
        if (t->time < timers->base)
                t->time = timers->base;
        /* Added in the past?  Treat it as imminent. */
        if (t->time < timers->base)
                t->time = timers->base;
-#endif
+
        if (t->time < timers->first)
                timers->first = t->time;
 
        if (t->time < timers->first)
                timers->first = t->time;
 
@@ -346,7 +343,11 @@ struct timer *timers_expire(struct timers *timers, struct timemono expire)
        unsigned int off;
        struct timer *t;
 
        unsigned int off;
        struct timer *t;
 
-       assert(now >= timers->base);
+       /* This can happen without TIME_HAVE_MONOTONIC, but I also have
+        * a report of OpenBSD 6.8 under virtualbox doing this. */
+       if (now < timers->base) {
+               return NULL;
+       }
 
        if (!timers->level[0]) {
                if (list_empty(&timers->far))
 
        if (!timers->level[0]) {
                if (list_empty(&timers->far))
diff --git a/ccan/ungraph/LICENSE b/ccan/ungraph/LICENSE
new file mode 120000 (symlink)
index 0000000..2354d12
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/ungraph/_info b/ccan/ungraph/_info
new file mode 100644 (file)
index 0000000..6088c5e
--- /dev/null
@@ -0,0 +1,77 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * ungraph - extract a graph from an ASCII diagram.
+ *
+ * This code takes an ASCII diagram and converts it to a graph.
+ * The following things are assumed:
+ * 1. The input consists of \n-terminated lines
+ * 2. /-\|+ are used for edges.
+ * 3. <^>v are used for arrowheads.
+ * 4. + can be used to cross-over.
+ * 5. No arrowheads or both-ended arrowheads are shortcuts for "both ways".
+ * 6. Edges can turn with or without a +, by up to 90 degrees.
+ * 7. Edges must go from one node name to another.
+ * 8. Any other text is an edge label which must be next to an edge or
+ *    another label.
+ *
+ * License: BSD-MIT
+ * Example:
+ * // Convert an ASCII graph to Graphviz dot format
+ * #include <ccan/err/err.h>
+ * #include <ccan/grab_file/grab_file.h>
+ * #include <ccan/ungraph/ungraph.h>
+ *
+ * // Just return the name as our node.
+ * static void *add_node(const tal_t *ctx,
+ *                       const char *name,
+ *                       const char **errstr,
+ *                       void *unused)
+ * {
+ *         return (void *)name;
+ * }
+ *
+ * static const char *add_edge(const tal_t *ctx,
+ *                             void *source_node,
+ *                             void *dest_node,
+ *                             bool bidir,
+ *                             const char **labels,
+ *                             void *arg)
+ * {
+ *         printf("%s -> %s;\n", 
+ *                (char *)source_node, (char *)dest_node);
+ *         if (bidir)
+ *                 printf("%s -> %s;\n", 
+ *                        (char *)dest_node, (char *)source_node);
+ *         return NULL;
+ * }
+ *
+ * int main(int argc, char *argv[])
+ * {
+ *         const char *graph = grab_file(NULL, argv[1], NULL), *errmsg;
+ *        printf("digraph %s {\n", argv[1] ? argv[1] : "stdin");
+ *         errmsg = ungraph(NULL, graph, add_node, add_edge, NULL);
+ *         if (errmsg)
+ *                 errx(1, "%s", errmsg);
+ *         printf("}");
+ * }
+ *
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/tal\n");
+               printf("ccan/tal/str\n");
+               printf("ccan/typesafe_cb\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/ungraph/test/run.c b/ccan/ungraph/test/run.c
new file mode 100644 (file)
index 0000000..a9ae9be
--- /dev/null
@@ -0,0 +1,140 @@
+#include <ccan/ungraph/ungraph.h>
+/* Include the C files directly. */
+#include <ccan/ungraph/ungraph.c>
+#include <ccan/tap/tap.h>
+#include <assert.h>
+
+static void *add_node(const tal_t *ctx,
+                     const char *name,
+                     const char **errstr,
+                     char **out)
+{
+       tal_append_fmt(out, "add_node %s\n", (char *)name);
+       return (void *)tal_steal(ctx, name);
+}
+
+static const char *add_edge(const tal_t *ctx,
+                           void *source_node,
+                           void *dest_node,
+                           bool bidir,
+                           const char **labels,
+                           char **out)
+{
+       tal_append_fmt(out, "add_edge %s-%s bidir=%i\n",
+                      (char *)source_node,
+                      (char *)dest_node,
+                      bidir);
+       for (size_t i = 0; i < tal_count(labels); i++)
+               tal_append_fmt(out, "- label %s\n", labels[i]);
+       return NULL;
+}
+
+int main(void)
+{
+       const tal_t *ctx = tal(NULL, char);
+       char *out = tal_arrz(ctx, char, 1);
+       /* This is how many tests you plan to run */
+       plan_tests(16);
+
+       ok1(ungraph(ctx,
+                   "AAA----->BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=0\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA<------BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge BBB-AAA bidir=0\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA------BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=1\n") == 0);
+
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA<------>BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=1\n") == 0);
+
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA\n"
+                   " ^ \n"
+                   " | \n"
+                   " | \n"
+                   " v \n"
+                   "BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=1\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA\n"
+                   " / \n"
+                   "| \n"
+                   " \\ \n"
+                   "  v \n"
+                   "  BBB\n",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=0\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA\n"
+                   " / \n"
+                   "|xyx \n"
+                   " \\ \n"
+                   "  v \n"
+                   "  BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=0\n"
+                  "- label xyx\n") == 0);
+
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   " AAA \n"
+                   "  |   \n"
+                   "A-+----B \n"
+                   "  | LABEL  \n"
+                   "  |  xyz\n"
+                   "  v  \n"
+                   " BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_node A\n"
+                  "add_node B\n"
+                  "add_edge AAA-BBB bidir=0\n"
+                  "add_edge A-B bidir=1\n"
+                  "- label LABEL\n"
+                  "- label xyz\n") == 0);
+
+       tal_free(ctx);
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/ungraph/ungraph.c b/ccan/ungraph/ungraph.c
new file mode 100644 (file)
index 0000000..ba29d22
--- /dev/null
@@ -0,0 +1,721 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include <ccan/ungraph/ungraph.h>
+#include <ccan/tal/str/str.h>
+
+struct xy {
+       size_t x, y;
+};
+
+struct text {
+       struct xy xy;
+       size_t width;
+       const char *text;
+       /* If it's a node, this is non-NULL */
+       void *node;
+       /* NULL if none found, edge it one found, self if > 1 */
+       struct edge *nearest_edge;
+};
+
+struct edge {
+       struct text *src, *dst;
+       bool bidir;
+       const char **labels;
+};
+
+/* This means we actually found two "nearest_edge" */
+static struct edge fake_edge;
+
+#define EDGES "+/-\\|"
+#define ARROWS "<^>v"
+
+enum dir {
+       UP,
+       UP_RIGHT,
+       RIGHT,
+       DOWN_RIGHT,
+       DOWN,
+       DOWN_LEFT,
+       LEFT,
+       UP_LEFT,
+       INVALID,
+};
+
+static enum dir opposite_dir(enum dir dir)
+{
+       return (dir + 4) % 8;
+}
+
+static enum dir clockwise(enum dir dir)
+{
+       return (dir + 1) % 8;
+}
+
+static enum dir anticlockwise(enum dir dir)
+{
+       return (dir + 7) % 8;
+}
+
+static enum dir dir_away(const struct text *t, struct xy xy)
+{
+       int xdir, ydir;
+       enum dir dirs[3][3] = {{UP_LEFT, UP, UP_RIGHT},
+                              {LEFT, INVALID, RIGHT},
+                              {DOWN_LEFT, DOWN, DOWN_RIGHT}};
+       
+       if (xy.y < t->xy.y)
+               ydir = -1;
+       else if (xy.y > t->xy.y)
+               ydir = 1;
+       else
+               ydir = 0;
+       if (xy.x >= t->xy.x + t->width)
+               xdir = 1;
+       else if (xy.x < t->xy.x)
+               xdir = -1;
+       else
+               xdir = 0;
+
+       return dirs[ydir+1][xdir+1];
+}
+
+static char line_for_dir(enum dir dir)
+{
+       switch (dir) {
+       case UP:
+       case DOWN:
+               return '|';
+       case UP_RIGHT:
+       case DOWN_LEFT:
+               return '/';
+       case RIGHT:
+       case LEFT:
+               return '-';
+       case DOWN_RIGHT:
+       case UP_LEFT:
+               return '\\';
+       case INVALID:
+               break;
+       }
+       abort();
+}
+
+static char arrow_for_dir(enum dir dir)
+{
+       switch (dir) {
+       case UP:
+       case UP_RIGHT:
+       case UP_LEFT:
+               return '^';
+       case DOWN:
+       case DOWN_RIGHT:
+       case DOWN_LEFT:
+               return 'v';
+       case LEFT:
+               return '<';
+       case RIGHT:
+               return '>';
+       case INVALID:
+               break;
+       }
+       abort();
+}
+
+static struct xy move_in_dir(struct xy xy, enum dir dir)
+{
+       switch (dir) {
+       case UP:
+               xy.y = xy.y - 1;
+               return xy;
+       case DOWN:
+               xy.y = xy.y + 1;
+               return xy;
+       case UP_RIGHT:
+               xy.x = xy.x + 1;
+               xy.y = xy.y - 1;
+               return xy;
+       case DOWN_LEFT:
+               xy.x = xy.x - 1;
+               xy.y = xy.y + 1;
+               return xy;
+       case RIGHT:
+               xy.x = xy.x + 1;
+               return xy;
+       case LEFT:
+               xy.x = xy.x - 1;
+               return xy;
+       case DOWN_RIGHT:
+               xy.x = xy.x + 1;
+               xy.y = xy.y + 1;
+               return xy;
+       case UP_LEFT:
+               xy.x = xy.x - 1;
+               xy.y = xy.y - 1;
+               return xy;
+       case INVALID:
+               break;
+       }
+       abort();
+}
+
+static char *sqc(char **sq, struct xy xy)
+{
+       return &sq[xy.y][xy.x];
+}
+
+/* Try straight ahead first, then a bit to either side, then
+ * finally left and right */
+static struct xy scan_move_next(struct xy xy, enum dir start, enum dir *cur)
+{
+       if (*cur == start)
+               *cur = clockwise(start);
+       else if (*cur == clockwise(start))
+               *cur = anticlockwise(start);
+       else if (*cur == anticlockwise(start))
+               *cur = anticlockwise(anticlockwise(start));
+       else if (*cur == anticlockwise(anticlockwise(start)))
+               *cur = clockwise(clockwise(start));
+       else {
+               *cur = INVALID;
+               return xy;
+       }
+       return move_in_dir(xy, *cur);
+}
+
+static void start_perimeter(struct xy *xyp, enum dir *dirp, struct xy xy)
+{
+       *dirp = RIGHT;
+       xyp->x = xy.x - 1;
+       xyp->y = xy.y - 1;
+}
+
+static void next_perimeter(struct xy *xyp, enum dir *dirp, struct xy xy, size_t width)
+{
+       *xyp = move_in_dir(*xyp, *dirp);
+       if (*dirp == RIGHT && xyp->x == xy.x + width)
+               *dirp = DOWN;
+       else if (*dirp == DOWN && xyp->y == xy.y + 1)
+               *dirp = LEFT;
+       else if (*dirp == LEFT && xyp->x == xy.x - 1)
+               *dirp = UP;
+       else if (*dirp == UP && xyp->y == xy.y - 2)
+               *dirp = INVALID;
+}
+
+/* Useful iterators. */
+#define for_each_scan_dir(xyp, dirp, xy, dir)                  \
+       for (*dirp = dir, *xyp = move_in_dir(xy, *dirp);        \
+            *dirp != INVALID;                                  \
+            *xyp = scan_move_next(xy, dir, dirp))
+
+#define for_each_perimeter(xyp, dirp, xy, width)       \
+       for (start_perimeter(xyp, dirp, xy);            \
+            *dirp != INVALID;                          \
+            next_perimeter(xyp, dirp, xy, width))
+
+/* Canonicalizes str into array of characters, finds text strings. */
+static char **square(const tal_t *ctx,
+                    const char *str,
+                    struct text **texts)
+{
+       size_t width = 0, height = 0;
+       size_t line_len = 0;
+       char **sq;
+       struct text *cur_text;
+       size_t strlen;
+
+       *texts = tal_arr(ctx, struct text, 0);
+
+       strlen = 0;
+       for (size_t i = 0; str[i]; i++) {
+               if (str[i] == '\n') {
+                       height++;
+                       line_len = 0;
+               } else {
+                       line_len++;
+                       if (line_len > width)
+                               width = line_len;
+               }
+               strlen++;
+       }
+
+       /* If didn't end in \n, it's implied */
+       if (line_len != 0) {
+               height++;
+               strlen++;
+       }
+
+       /* For analysis simplicity, create a blank border. */
+       sq = tal_arr(ctx, char *, height + 2);
+       for (size_t i = 0; i < height + 2; i++) {
+               sq[i] = tal_arr(sq, char, width + 2);
+               memset(sq[i], ' ', width + 2);
+       }
+
+       /* Copy across and find text */
+       cur_text = NULL;
+       width = height = 1;
+       for (size_t i = 0; i < strlen; i++) {
+               bool end_text;
+               bool eol;
+
+               eol = (str[i] == '\n' || str[i] == '\0');
+               if (!eol)
+                       sq[height][width] = str[i];
+
+               /* v by itself handled separately below */
+               if (strchr(EDGES ARROWS "\n", str[i]) && str[i] != 'v') {
+                       end_text = (cur_text != NULL);
+               } else if (cur_text) {
+                       /* Two spaces ends text */
+                       end_text = (str[i] == ' ' && str[i-1] == ' ') || eol;
+               } else if (str[i] != ' ') {
+                       size_t num_texts = tal_count(*texts);
+                       tal_resize(texts, num_texts+1);
+                       cur_text = &(*texts)[num_texts];
+                       cur_text->xy.x = width;
+                       cur_text->xy.y = height;
+                       cur_text->width = 0;
+                       cur_text->node = NULL;
+                       cur_text->nearest_edge = NULL;
+                       end_text = false;
+               } else
+                       end_text = false;
+
+               if (end_text) {
+                       /* Trim final space */
+                       if (sq[cur_text->xy.y][cur_text->xy.x + cur_text->width-1] == ' ')
+                               cur_text->width--;
+                       /* Ignore lone 'v' */
+                       if (cur_text->width == 1 && sq[cur_text->xy.y][cur_text->xy.x] == 'v')
+                               tal_resize(texts, tal_count(*texts)-1);
+                       else {
+                               cur_text->text = tal_strndup(ctx, &sq[cur_text->xy.y][cur_text->xy.x],
+                                                            cur_text->width);
+                       }
+                       cur_text = NULL;
+               }
+
+               if (cur_text)
+                       cur_text->width++;
+               if (eol) {
+                       height++;
+                       width = 1;
+               } else
+                       width++;
+       }
+
+       return sq;
+}
+
+/* If text was not previously a node, it is now! */
+static const char *text_now_node(const tal_t *ctx,
+                                char **sq,
+                                struct text *text,
+                                void *(*add_node)(const tal_t *ctx,
+                                                  const char *name,
+                                                  const char **errstr,
+                                                  void *arg),
+                                void *arg)
+{
+       const char *err;
+
+       /* Already a node? */
+       if (text->node)
+               return NULL;
+
+       text->node = add_node(ctx, text->text, &err, arg);
+       if (!text->node)
+               return err;
+       return NULL;
+}
+
+static bool correct_line_char(char c, enum dir dir, enum dir *newdir)
+{
+       if (c == line_for_dir(dir)) {
+               *newdir = dir;
+               return true;
+       } else if (c == line_for_dir(anticlockwise(dir))) {
+               *newdir = anticlockwise(dir);
+               return true;
+       } else if (c == line_for_dir(clockwise(dir))) {
+               *newdir = clockwise(dir);
+               return true;
+       }
+       return false;
+}
+
+static bool seek_line(char **sq, struct xy *xy, enum dir *dir)
+{
+       struct xy scan;
+       enum dir scandir;
+
+       for_each_scan_dir(&scan, &scandir, *xy, *dir) {
+               if (correct_line_char(*sqc(sq, scan), scandir, &scandir))
+                       goto found;
+               /* + in front always works */
+               if (*dir == scandir && *sqc(sq, scan) == '+')
+                       goto found;
+       }
+       return false;
+
+found:
+       *xy = scan;
+       *dir = scandir;
+       return true;
+}
+
+static bool seek_arrowhead(char **sq, struct xy *xy, enum dir *dir)
+{
+       struct xy scan;
+       enum dir scandir;
+
+       for_each_scan_dir(&scan, &scandir, *xy, *dir) {
+               if (strchr(ARROWS, *sqc(sq, scan))) {
+                       *xy = scan;
+                       *dir = scandir;
+                       return true;
+               }
+       }
+       return false;
+}
+
+static struct text *in_text(struct text *texts, struct xy xy)
+{
+       for (size_t i = 0; i < tal_count(texts); i++) {
+               if (texts[i].xy.y != xy.y)
+                       continue;
+               if (xy.x >= texts[i].xy.x + texts[i].width)
+                       continue;
+               if (xy.x < texts[i].xy.x)
+                       continue;
+               return texts + i;
+       }
+       return NULL;
+}
+
+static struct text *seek_text(struct text *texts,
+                             struct xy xy, enum dir dir)
+{
+       struct xy scan;
+       enum dir scandir;
+
+       for_each_scan_dir(&scan, &scandir, xy, dir) {
+               struct text *t = in_text(texts, scan);
+               if (t)
+                       return t;
+       }
+       return NULL;
+}
+
+static void erase_line(char **sq,
+                      struct xy xy,
+                      enum dir dir,
+                      enum dir prev_dir)
+{
+       char c = ' ';
+
+       /* If we go straight through a +, convert for crossover */
+       if (prev_dir == dir && *sqc(sq, xy) == '+') {
+               if (dir == UP || dir == DOWN)
+                       c = '-';
+               if (dir == LEFT || dir == RIGHT)
+                       c = '|';
+       }
+       *sqc(sq, xy) = c;
+}
+
+static bool in_nearby(struct text **nearby, const struct text *t)
+{
+       for (size_t i = 0; i < tal_count(nearby); i++) {
+               if (nearby[i] == t)
+                       return true;
+       }
+       return false;
+}
+
+static void add_nearby(struct text ***nearby,
+                      struct text *texts,
+                      struct xy xy)
+{
+       struct xy perim;
+       enum dir pdir;
+       size_t n = tal_count(*nearby);
+
+       for_each_perimeter(&perim, &pdir, xy, 1) {
+               struct text *t = in_text(texts, perim);
+               if (!t)
+                       continue;
+               /* Don't care if it's already a node */
+               if (t->node)
+                       continue;
+               if (in_nearby(*nearby, t))
+                       continue;
+               tal_resize(nearby, n+1);
+               (*nearby)[n++] = t;
+       }
+}
+
+/* Clears lines as it goes. */
+static struct text *follow_line(char **sq,
+                               struct text *texts,
+                               struct xy xy,
+                               enum dir dir,
+                               bool *arrow_src,
+                               bool *arrow_dst,
+                               bool *dangling,
+                               struct text ***nearby)
+{
+       char expect_arrow = arrow_for_dir(opposite_dir(dir));
+       enum dir prev_dir;
+
+       *nearby = tal_arr(sq, struct text *, 0);
+
+       if (*sqc(sq, xy) == expect_arrow) {
+               *arrow_src = true;
+       } else if (*sqc(sq, xy) == line_for_dir(dir)) {
+               *arrow_src = false;
+       } else if (*sqc(sq, xy) == line_for_dir(anticlockwise(dir))) {
+               *arrow_src = false;
+               dir = anticlockwise(dir);
+       } else if (*sqc(sq, xy) == line_for_dir(clockwise(dir))) {
+               *arrow_src = false;
+               dir = clockwise(dir);
+       } else {
+               *dangling = false;
+               /* No arrow is fine. */
+               return NULL;
+       }
+
+       erase_line(sq, xy, dir, INVALID);
+       add_nearby(nearby, texts, xy);
+
+       *arrow_dst = false;
+       prev_dir = dir;
+       for (;;) {
+               /* Try to continue line */
+               if (!*arrow_dst && seek_line(sq, &xy, &dir)) {
+                       erase_line(sq, xy, dir, prev_dir);
+                       add_nearby(nearby, texts, xy);
+                       prev_dir = dir;
+                       continue;
+               }
+               /* Look for arrow */
+               if (!*arrow_dst && seek_arrowhead(sq, &xy, &dir)) {
+                       erase_line(sq, xy, dir, prev_dir);
+                       add_nearby(nearby, texts, xy);
+                       *arrow_dst = true;
+                       prev_dir = dir;
+                       continue;
+               }
+               break;
+       }
+
+       /* Must be in text! */
+       *dangling = true;
+       return seek_text(texts, xy, dir);
+}
+
+static const char *try_create_edge(const tal_t *ctx,
+                                  char **sq,
+                                  struct text *texts,
+                                  struct xy xy,
+                                  struct text *src,
+                                  void *(*add_node)(const tal_t *ctx,
+                                                    const char *name,
+                                                    const char **errstr,
+                                                    void *arg),
+                                  void *arg,
+                                  struct edge **edge)
+{
+       struct text *dst;
+       bool arrow_src, arrow_dst, dangling;
+       struct text **nearby;
+       const char *err;
+
+       *edge = NULL;
+       if (in_text(texts, xy))
+               return NULL;
+
+       dst = follow_line(sq, texts, xy, dir_away(src, xy), &arrow_src, &arrow_dst, &dangling, &nearby);
+       if (!dst) {
+               if (dangling)
+                       return tal_fmt(ctx, "Found dangling arrow at (%zu,%zu)", xy.x-1, xy.y-1);
+               return NULL;
+       }
+
+       /* If you weren't a node before, you are now! */
+       err = text_now_node(ctx, sq, src, add_node, arg);
+       if (err)
+               return err;
+       err = text_now_node(ctx, sq, dst, add_node, arg);
+       if (err)
+               return err;
+
+       /* No arrows equiv to both arrows */
+       if (!arrow_src && !arrow_dst)
+               arrow_src = arrow_dst = true;
+
+       *edge = tal(NULL, struct edge);
+       if (arrow_dst) {
+               (*edge)->src = src;
+               (*edge)->dst = dst;
+               (*edge)->bidir = arrow_src;
+       } else {
+               (*edge)->src = dst;
+               (*edge)->dst = src;
+               (*edge)->bidir = false;
+       }
+       (*edge)->labels = tal_arr(*edge, const char *, 0);
+
+       /* Now record any texts it passed by, in case they're labels */
+       for (size_t i = 0; i < tal_count(nearby); i++) {
+               /* We might have just made it a node */
+               if (nearby[i]->node)
+                       continue;
+               /* Already has an edge?  Mark it as near two, to error
+                * later if it's a label */
+               if (nearby[i]->nearest_edge)
+                       nearby[i]->nearest_edge = &fake_edge;
+               else
+                       nearby[i]->nearest_edge = *edge;
+       }
+               
+       return NULL;
+}
+
+static const char *scan_for_unused(const tal_t *ctx,
+                                  struct text *texts,
+                                  char **sq)
+{
+       struct xy xy;
+       for (xy.y = 0; xy.y < tal_count(sq); xy.y++) {
+               for (xy.x = 0; xy.x < tal_count(sq[xy.y]); xy.x++) {
+                       if (in_text(texts, xy))
+                               continue;
+                       if (*sqc(sq,xy) != ' ')
+                               return tal_fmt(ctx, "Unused '%c' at (%zu,%zu)",
+                                              *sqc(sq, xy), xy.x-1, xy.y-1);
+               }
+       }
+       return NULL;
+}
+
+static void add_label(struct edge *edge, const struct text *label)
+{
+       size_t n = tal_count(edge->labels);
+       tal_resize(&edge->labels, n+1);
+       edge->labels[n] = label->text;
+}
+       
+const char *ungraph_(const tal_t *ctx,
+                    const char *str,
+                    void *(*add_node)(const tal_t *ctx,
+                                      const char *name,
+                                      const char **errstr,
+                                      void *arg),
+                    const char *(*add_edge)(const tal_t *ctx,
+                                            void *source_node,
+                                            void *dest_node,
+                                            bool bidir,
+                                            const char **labels,
+                                            void *arg),
+                    void *arg)
+{
+       /* To hold all our temporaries! */
+       const tal_t *sub = tal(ctx, char);
+       char **sq;
+       struct text *texts, *remaining_label;
+       const char *err;
+       bool progress;
+       struct edge **edges = tal_arr(sub, struct edge *, 0);
+       size_t num_edges = 0;
+
+       /* We create canonical square, find texts. */
+       sq = square(sub, str, &texts);
+
+       /* Now search for arrows around each text, cleaning
+        * as we go! */
+       for (size_t i = 0; i < tal_count(texts); i++) {
+               struct xy perim;
+               enum dir pdir;
+               struct text *t = &texts[i];
+
+               for_each_perimeter(&perim, &pdir, t->xy, t->width) {
+                       struct edge *edge;
+                       err = try_create_edge(ctx, sq, texts, perim, t, add_node, arg, &edge);
+                       if (err)
+                               goto fail;
+                       if (edge) {
+                               tal_resize(&edges, num_edges+1);
+                               edges[num_edges++] = tal_steal(edges, edge);
+                       }
+               }
+       }
+
+       /* Now attach any remaining labels */
+       for (size_t i = 0; i < tal_count(texts); i++) {
+               struct text *t = &texts[i];
+
+               if (t->node)
+                       continue;
+               if (t->nearest_edge == &fake_edge) {
+                       err = tal_fmt(ctx, "Label at (%zu,%zu) near more than one edge",
+                                     t->xy.x-1, t->xy.y-1);
+                       goto fail;
+               }
+               if (t->nearest_edge)
+                       add_label(t->nearest_edge, t);
+       }
+
+       /* Any remaining labels must be attached to already-attached labels */
+       do {
+               progress = false;
+               remaining_label = NULL;
+
+               for (size_t i = 0; i < tal_count(texts); i++) {
+                       struct xy perim;
+                       enum dir pdir;
+                       struct text *t = &texts[i];
+
+                       if (t->node || t->nearest_edge)
+                               continue;
+
+                       remaining_label = t;
+                       for_each_perimeter(&perim, &pdir, t->xy, t->width) {
+                               struct text *neighbor = in_text(texts, perim);
+                               if (!neighbor || neighbor->node || !neighbor->nearest_edge)
+                                       continue;
+                               t->nearest_edge = neighbor->nearest_edge;
+                               add_label(t->nearest_edge, t);
+                               progress = true;
+                               break;
+                       }
+               }
+       } while (progress);
+
+       if (remaining_label) {
+               err = tal_fmt(ctx, "Label at (%zu,%zu) not near any edge",
+                             remaining_label->xy.x-1,
+                             remaining_label->xy.y-1);
+               goto fail;
+       }
+
+       err = scan_for_unused(ctx, texts, sq);
+       if (err)
+               goto fail;
+
+       /* Now add edges, complete with labels */
+       for (size_t i = 0; i < tal_count(edges); i++) {
+               err = add_edge(ctx, edges[i]->src->node, edges[i]->dst->node,
+                              edges[i]->bidir, edges[i]->labels, arg);
+               if (err)
+                       goto fail;
+       }
+       
+       tal_free(sub);
+       return NULL;
+
+fail:
+       tal_free(sub);
+       return err;
+}
diff --git a/ccan/ungraph/ungraph.h b/ccan/ungraph/ungraph.h
new file mode 100644 (file)
index 0000000..8480b35
--- /dev/null
@@ -0,0 +1,53 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_UNGRAPH_H
+#define CCAN_UNGRAPH_H
+#include <ccan/tal/tal.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
+
+/**
+ * ungraph: extract a graph from an ASCII graph.
+ * @ctx: context for callbacks, and/or returned errstr.
+ * @str: a string containing a graph.
+ * @add_node: callback for a new node, returns node.
+ * @add_edge: callback for a new edge, with tal_count(labels).
+ * @arg: callback argument.
+ *
+ * On success, returns NULL.  On failure, returns some error message
+ * (allocated off @ctx, or returned from callbacks).
+ *
+ * If @add_node returns NULL, it must set @errstr. @add_edge
+ * returns the error message directly.
+ *
+ * @add_node and @add_edge can tal_steal the name/labels if they want,
+ * otherwise they will be freed.
+ */
+const char *ungraph_(const tal_t *ctx,
+                    const char *str,
+                    void *(*add_node)(const tal_t *ctx,
+                                      const char *name,
+                                      const char **errstr,
+                                      void *arg),
+                    const char *(*add_edge)(const tal_t *ctx,
+                                            void *source_node,
+                                            void *dest_node,
+                                            bool bidir,
+                                            const char **labels,
+                                            void *arg),
+                    void *arg);
+
+#define ungraph(ctx, str, add_node, add_edge, arg)                     \
+       ungraph_((ctx), (str),                                          \
+                typesafe_cb_preargs(void *, void *,                    \
+                                    (add_node), (arg),                 \
+                                    const tal_t *,                     \
+                                    const char *,                      \
+                                    const char **errstr),              \
+                typesafe_cb_preargs(const char *, void *,              \
+                                    (add_edge), (arg),                 \
+                                    const tal_t *,                     \
+                                    void *,                            \
+                                    void *,                            \
+                                    bool,                              \
+                                    const char **),                    \
+                arg)
+#endif /* CCAN_UNGRAPH_H */
index 34ecb1d19f23da32c747a6863cb45a72acef82e4..7b9917775e42a4b26fc7e31480dea162811d20b7 100644 (file)
@@ -117,7 +117,7 @@ test_unicode_scalar_value(void) {
   char src[4];
 
   /* Unicode scalar value [U+0000, U+007F] */
   char src[4];
 
   /* Unicode scalar value [U+0000, U+007F] */
-  for (ord = 0x0000; ord <= 0x007F; ord++) {
+  for (ord = 0x0001; ord <= 0x007F; ord++) {
     encode_ord(ord, 1, src);
     TEST_UTF8(src, 1, ord ? 0 : ERANGE);
   }
     encode_ord(ord, 1, src);
     TEST_UTF8(src, 1, ord ? 0 : ERANGE);
   }
@@ -255,7 +255,7 @@ test_continuations(void) {
 int
 main(int argc, char **argv)
 {
 int
 main(int argc, char **argv)
 {
-  plan_tests(2190906);
+  plan_tests(2190906 - 1);
   test_unicode_scalar_value();
   test_surrogates();
   test_non_shortest_form();
   test_unicode_scalar_value();
   test_surrogates();
   test_non_shortest_form();
index 346d2d95b72f98dc2d1a32ce7884091ccbd200d5..cb18041a5d35cb9f2486388e828d3cd6f77d19c0 100644 (file)
@@ -63,6 +63,8 @@ bool utf8_decode(struct utf8_state *utf8_state, char c)
                /* First character in sequence. */
                if (((unsigned char)c & 0x80) == 0) {
                        /* ASCII, easy. */
                /* First character in sequence. */
                if (((unsigned char)c & 0x80) == 0) {
                        /* ASCII, easy. */
+                       if (c == 0)
+                               goto bad_encoding;
                        utf8_state->total_len = 1;
                        utf8_state->c = c;
                        goto finished_decoding;
                        utf8_state->total_len = 1;
                        utf8_state->c = c;
                        goto finished_decoding;
index a095f02e870baf53d0020fd60ffecabc8a0b0cba..9a74696800cef1d8a5100703e6939d77d06e0343 100644 (file)
@@ -33,7 +33,7 @@ static inline void utf8_state_init(struct utf8_state *utf8_state)
  * Otherwise returns true, @utf8_state can be reused without initializeation,
  * and sets errno:
  * 0: success
  * Otherwise returns true, @utf8_state can be reused without initializeation,
  * and sets errno:
  * 0: success
- * EINVAL: bad encoding.
+ * EINVAL: bad encoding (including a NUL character).
  * EFBIG: not a minimal encoding.
  * ERANGE: encoding of invalid character.
  *
  * EFBIG: not a minimal encoding.
  * ERANGE: encoding of invalid character.
  *
index 8820f17410293fcb9763b63272f41d5702d8ac13..9e805a3a387cc4c083aeaadce450c8baa53f854b 100644 (file)
@@ -55,7 +55,7 @@ static inline uint16_t version_minor(const struct version v) {
  */
 static inline struct version version(uint16_t major, uint16_t minor)
 {
  */
 static inline struct version version(uint16_t major, uint16_t minor)
 {
-       struct version v = { ._v = major << 16 | minor };
+       struct version v = { ._v = (uint32_t)major << 16 | minor };
        return v;
 }
 
        return v;
 }
 
index f3d1a5ba13b36354d23bdf309ae28d8c75617bd3..3f88bbcdf830d0ca7afbd9e4214936e6940a1f9d 100644 (file)
@@ -151,11 +151,12 @@ static void reap_output(void)
                        int old_len, len;
                        /* This length includes nul terminator! */
                        old_len = tal_count(c->output);
                        int old_len, len;
                        /* This length includes nul terminator! */
                        old_len = tal_count(c->output);
-                       tal_resize(&c->output, old_len + 1024);
-                       len = read(c->output_fd, c->output + old_len - 1, 1024);
+                       tal_resize(&c->output, old_len + 65536);
+                       len = read(c->output_fd, c->output + old_len - 1, 65536);
                        if (len < 0)
                                err(1, "Reading from async command");
                        if (len < 0)
                                err(1, "Reading from async command");
-                       tal_resize(&c->output, old_len + len);
+                       if (len != 65536)
+                               tal_resize(&c->output, old_len + len);
                        c->output[old_len + len - 1] = '\0';
                        if (len == 0) {
                                struct rusage ru;
                        c->output[old_len + len - 1] = '\0';
                        if (len == 0) {
                                struct rusage ru;
index 60d203763cf99417ba818c6dbdd7ad219b03253c..b921fa6279b3da5e1aa36ccae10a85ad5f7df2e3 100644 (file)
@@ -30,7 +30,7 @@ struct license_info {
 };
 
 /* Is [project license][file license] compatible? */
 };
 
 /* Is [project license][file license] compatible? */
-bool license_compatible[LICENSE_UNKNOWN+1][LICENSE_UNKNOWN];
+extern bool license_compatible[LICENSE_UNKNOWN+1][LICENSE_UNKNOWN];
 
 extern const struct license_info licenses[];
 
 
 extern const struct license_info licenses[];
 
index c8b131cc708f214d61b269b18ab8b081fe51e685..7d8f6b095de4b6e615591b6fb37319a20fe603a3 100644 (file)
@@ -142,6 +142,9 @@ static const struct test base_tests[] = {
        { "HAVE_ATTRIBUTE_NONNULL", "__attribute__((nonnull)) support",
          "DEFINES_FUNC", NULL, NULL,
          "static char *__attribute__((nonnull)) func(char *p) { return p; }" },
        { "HAVE_ATTRIBUTE_NONNULL", "__attribute__((nonnull)) support",
          "DEFINES_FUNC", NULL, NULL,
          "static char *__attribute__((nonnull)) func(char *p) { return p; }" },
+       { "HAVE_ATTRIBUTE_RETURNS_NONNULL", "__attribute__((returns_nonnull)) support",
+         "DEFINES_FUNC", NULL, NULL,
+         "static const char *__attribute__((returns_nonnull)) func(void) { return \"hi\"; }" },
        { "HAVE_ATTRIBUTE_SENTINEL", "__attribute__((sentinel)) support",
          "DEFINES_FUNC", NULL, NULL,
          "static int __attribute__((sentinel)) func(int i, ...) { return i; }" },
        { "HAVE_ATTRIBUTE_SENTINEL", "__attribute__((sentinel)) support",
          "DEFINES_FUNC", NULL, NULL,
          "static int __attribute__((sentinel)) func(int i, ...) { return i; }" },
@@ -194,7 +197,7 @@ static const struct test base_tests[] = {
          "return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
        { "HAVE_BUILTIN_CTZ", "__builtin_ctz support",
          "INSIDE_MAIN", NULL, NULL,
          "return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
        { "HAVE_BUILTIN_CTZ", "__builtin_ctz support",
          "INSIDE_MAIN", NULL, NULL,
-         "return __builtin_ctz(1 << (sizeof(int)*8 - 1)) == (sizeof(int)*8 - 1) ? 0 : 1;" },
+         "return __builtin_ctz(1U << (sizeof(int)*8 - 1)) == (sizeof(int)*8 - 1) ? 0 : 1;" },
        { "HAVE_BUILTIN_CTZL", "__builtin_ctzl support",
          "INSIDE_MAIN", NULL, NULL,
          "return __builtin_ctzl(1UL << (sizeof(long)*8 - 1)) == (sizeof(long)*8 - 1) ? 0 : 1;" },
        { "HAVE_BUILTIN_CTZL", "__builtin_ctzl support",
          "INSIDE_MAIN", NULL, NULL,
          "return __builtin_ctzl(1UL << (sizeof(long)*8 - 1)) == (sizeof(long)*8 - 1) ? 0 : 1;" },
@@ -411,7 +414,7 @@ static const struct test base_tests[] = {
          "int main(int argc, char *argv[]) {\n"
          "     (void)argc;\n"
          "     char pad[sizeof(int *) * 1];\n"
          "int main(int argc, char *argv[]) {\n"
          "     (void)argc;\n"
          "     char pad[sizeof(int *) * 1];\n"
-         "     strncpy(pad, argv[0], sizeof(pad));\n"
+         "     memcpy(pad, argv[0], sizeof(pad));\n"
          "     int *x = (int *)pad, *y = (int *)(pad + 1);\n"
          "     return *x == *y;\n"
          "}\n" },
          "     int *x = (int *)pad, *y = (int *)(pad + 1);\n"
          "     return *x == *y;\n"
          "}\n" },
@@ -468,7 +471,7 @@ static const struct test base_tests[] = {
          "#include <stddef.h>\n"
          "#include <ucontext.h>\n"
          "static int worked = 0;\n"
          "#include <stddef.h>\n"
          "#include <ucontext.h>\n"
          "static int worked = 0;\n"
-         "static char stack[1024];\n"
+         "static char stack[8192];\n"
          "static ucontext_t a, b;\n"
          "static void fn(void *p, void *q) {\n"
          "     void *cp = &worked;\n"
          "static ucontext_t a, b;\n"
          "static void fn(void *p, void *q) {\n"
          "     void *cp = &worked;\n"
@@ -495,6 +498,43 @@ static const struct test base_tests[] = {
          "     return __builtin_cpu_supports(\"mmx\");\n"
          "}"
        },
          "     return __builtin_cpu_supports(\"mmx\");\n"
          "}"
        },
+       { "HAVE_CLOSEFROM", "closefrom() offered by system",
+         "DEFINES_EVERYTHING", NULL, NULL,
+         "#include <stdlib.h>\n"
+         "#include <unistd.h>\n"
+         "int main(void) {\n"
+         "    closefrom(STDERR_FILENO + 1);\n"
+         "    return 0;\n"
+         "}\n"
+       },
+       { "HAVE_F_CLOSEM", "F_CLOSEM defined for fctnl.",
+         "DEFINES_EVERYTHING", NULL, NULL,
+         "#include <fcntl.h>\n"
+         "#include <unistd.h>\n"
+         "int main(void) {\n"
+         "    int res = fcntl(STDERR_FILENO + 1, F_CLOSEM, 0);\n"
+         "    return res < 0;\n"
+         "}\n"
+       },
+       { "HAVE_NR_CLOSE_RANGE", "close_range syscall available as __NR_close_range.",
+         "DEFINES_EVERYTHING", NULL, NULL,
+         "#include <limits.h>\n"
+         "#include <sys/syscall.h>\n"
+         "#include <unistd.h>\n"
+         "int main(void) {\n"
+         "    int res = syscall(__NR_close_range, STDERR_FILENO + 1, INT_MAX, 0);\n"
+         "    return res < 0;\n"
+         "}\n"
+       },
+       { "HAVE_F_MAXFD", "F_MAXFD defined for fcntl.",
+         "DEFINES_EVERYTHING", NULL, NULL,
+         "#include <fcntl.h>\n"
+         "#include <unistd.h>\n"
+         "int main(void) {\n"
+         "    int res = fcntl(0, F_MAXFD);\n"
+         "    return res < 0;\n"
+         "}\n"
+       },
 };
 
 static void c12r_err(int eval, const char *fmt, ...)
 };
 
 static void c12r_err(int eval, const char *fmt, ...)
@@ -757,14 +797,12 @@ static bool run_test(const char *cmd, const char *wrapper, struct test *test)
                /* We run INSIDE_MAIN tests for sanity checking. */
                if (strstr(test->style, "EXECUTE")
                    || strstr(test->style, "INSIDE_MAIN")) {
                /* We run INSIDE_MAIN tests for sanity checking. */
                if (strstr(test->style, "EXECUTE")
                    || strstr(test->style, "INSIDE_MAIN")) {
-                       char *cmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1);
+                       char *runcmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1);
 
 
-                       strcpy(cmd, wrapper);
-                       strcat(cmd, " ." DIR_SEP OUTPUT_FILE);
-                       output = run(cmd, &status);
-                       if (wrapper) {
-                               free(cmd);
-                       }
+                       strcpy(runcmd, wrapper);
+                       strcat(runcmd, " ." DIR_SEP OUTPUT_FILE);
+                       output = run(runcmd, &status);
+                       free(runcmd);
                        if (!strstr(test->style, "EXECUTE") && status != 0)
                                c12r_errx(EXIT_BAD_TEST,
                                          "Test for %s failed with %i:\n%s",
                        if (!strstr(test->style, "EXECUTE") && status != 0)
                                c12r_errx(EXIT_BAD_TEST,
                                          "Test for %s failed with %i:\n%s",
index da9ed0a3fad62bdd3330285ae67751ef8084c7d2..da3e00cd73ba7088981d7a40ad3b995c4ad48e2b 100644 (file)
@@ -96,10 +96,11 @@ char *read_config_header(const char *ccan_dir, bool verbose)
        char *config_header;
 
        config_header = grab_file(NULL, fname);
        char *config_header;
 
        config_header = grab_file(NULL, fname);
-       tal_free(fname);
 
 
-       if (!config_header)
+       if (!config_header) {
+               tal_free(fname);
                return NULL;
                return NULL;
+       }
 
        lines = tal_strsplit(config_header, config_header, "\n", STR_EMPTY_OK);
        for (i = 0; i < tal_count(lines) - 1; i++) {
 
        lines = tal_strsplit(config_header, config_header, "\n", STR_EMPTY_OK);
        for (i = 0; i < tal_count(lines) - 1; i++) {
@@ -129,5 +130,6 @@ char *read_config_header(const char *ccan_dir, bool verbose)
                                       fname, cflags);
                }
        }
                                       fname, cflags);
                }
        }
+       tal_free(fname);
        return config_header;
 }
        return config_header;
 }
index 3e9d445a3eaed74f7e5222b38adddfc3560fbbf3..c82a8bdb5d6176296c71dc859cf35d9927f8a878 100644 (file)
@@ -4,7 +4,7 @@ include('logo.html');
 include('menulist.html');
 include('static-configuration');
 
 include('menulist.html');
 include('static-configuration');
 
-$tarballsize=round((filesize($argv[3]."/ccan.tar.bz2") + 1023) / 1024);
+$tarballsize=round((filesize($argv[3]."/".$argv[4]) + 1023) / 1024);
 ?>
 <div class='content'>
 <h1> List of all CCAN modules: </h1>
 ?>
 <div class='content'>
 <h1> List of all CCAN modules: </h1>
@@ -16,7 +16,7 @@ download.
 </p>
 
 <p>
 </p>
 
 <p>
-Or you can just download the <a href="ccan.tar.bz2">tarball of everything including CCAN tools (<?=$tarballsize?>K)</a>.
+Or you can just download the <a href="<?=$argv[4]?>">tarball of everything including CCAN tools (<?=$tarballsize?>K)</a>.
 </p>
 
 <table align="center" cellpadding="3" cellspacing="1">
 </p>
 
 <table align="center" cellpadding="3" cellspacing="1">
@@ -25,7 +25,7 @@ Or you can just download the <a href="ccan.tar.bz2">tarball of everything includ
 <th align="right">Download</th>
 
 <?php 
 <th align="right">Download</th>
 
 <?php 
-$modules = array_slice($argv, 4);
+$modules = array_slice($argv, 5);
 sort($modules);
 
 foreach ($modules as $module) {
 sort($modules);
 
 foreach ($modules as $module) {
index 700714ba7fe1884d51547594887cadabe19266df..8ef968e0f1c74b2055fccb471f4fc363ae335d45 100644 (file)
@@ -21,7 +21,7 @@ for Perl code development and sharing.
 <h2>Get The Code</h2>
 
 <p> You can get each module as a tarball (<a href="list.html">see
 <h2>Get The Code</h2>
 
 <p> You can get each module as a tarball (<a href="list.html">see
-list</a>), get a tarball of <a href="ccan.tar.bz2">the whole repository</a> with tools,
+list</a>), get a tarball of <a href="<?=$argv[1]?>">the whole repository</a> with tools,
 or clone our <a href="http://git.ozlabs.org/?p=ccan">git repository</a> (<tt>git clone git://git.ozlabs.org/~ccan/ccan</tt>) or the one on <a href="http://github.com/rustyrussell/ccan/">github</a>.
 </p>
 
 or clone our <a href="http://git.ozlabs.org/?p=ccan">git repository</a> (<tt>git clone git://git.ozlabs.org/~ccan/ccan</tt>) or the one on <a href="http://github.com/rustyrussell/ccan/">github</a>.
 </p>