# 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
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 .
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.
-$(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)
- $(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 > $@
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 $< $@
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);
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)
int8_t ret;
ret = maps->decode_map[(unsigned char)b64letter];
- if (ret == (char)0xff) {
+ if (ret == '\xff') {
errno = EDOM;
return -1;
}
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])
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;
}
-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];
{
ssize_t dest_offset = 0;
ssize_t i;
- size_t more;
+ ssize_t more;
if (destlen < base64_decoded_length(srclen)) {
errno = EOVERFLOW;
* @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
* @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: */
* @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);
/*
* We wrap each word in a structure for type checking.
*/
-typedef struct {
+typedef struct bitmap {
bitmap_word w;
} bitmap;
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_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++)
- 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++)
- 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);
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
--- /dev/null
+../../licenses/CC0
\ No newline at end of file
--- /dev/null
+#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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+#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();
+}
#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
/**
--- /dev/null
+../../licenses/BSD-MIT
\ No newline at end of file
/* 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]);
}
/* 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;
}
} 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];
}
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);
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;
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];
- 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;
}
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;
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);
- 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);
}
* (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);
/*
--- /dev/null
+../../../licenses/CC0
\ No newline at end of file
--- /dev/null
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * crypto/xtea - implementation of xtea algorithm.
+ *
+ * This code is an implementation of the simple xtea encryption/decryption
+ * algorithm. You probably don't want to use this; try AES or chacha20
+ * from libsodium for modern encryption routines.
+ *
+ * License: CC0 (Public Domain)
+ */
+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;
+}
--- /dev/null
+#include <ccan/crypto/xtea/xtea.h>
+/* Include the C files directly. */
+#include <ccan/crypto/xtea/xtea.c>
+#include <ccan/tap/tap.h>
+#include <string.h>
+
+int main(void)
+{
+ uint64_t v, e;
+ struct xtea_secret s;
+
+ /* This is how many tests you plan to run */
+ plan_tests(66);
+
+ memset(&s, 1, sizeof(s));
+
+ for (v = 1; v; v <<= 1) {
+ e = xtea_encipher(&s, v);
+ ok1(xtea_decipher(&s, e) == v);
+ }
+
+ /* The only 32-iteration from the "test vectors" at
+ * http://www.cix.co.uk/~klockstone/teavect.htm:
+ * in=af20a390547571aa, N=32, k=27f917b1c1da899360e2acaaa6eb923d, out=d26428af0a202283
+ */
+ v = 0xaf20a390547571aaULL;
+ s.u.u32[0] = 0x27f917b1;
+ s.u.u32[1] = 0xc1da8993;
+ s.u.u32[2] = 0x60e2acaa;
+ s.u.u32[3] = 0xa6eb923d;
+ e = xtea_encipher(&s, v);
+ ok1(e == 0xd26428af0a202283ULL);
+ ok1(xtea_decipher(&s, e) == v);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
--- /dev/null
+/* CC0 license (public domain) - see LICENSE file for details */
+#include "xtea.h"
+
+/* Based on http://www.cix.co.uk/~klockstone/xtea.pdf, and the modernized
+ * source at https://en.wikipedia.org/wiki/XTEA */
+
+/* Each round below represents two rounds, so we usee 32 not 64 here */
+#define NUM_DOUBLE_ROUNDS 32
+
+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;
+ int 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]);
+ }
+ return ((uint64_t)v0 << 32) | v1;
+}
+
+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;
+ int 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]);
+ }
+ return ((uint64_t)v0 << 32) | v1;
+}
--- /dev/null
+/* CC0 license (public domain) - see LICENSE file for details */
+#ifndef CCAN_CRYPTO_XTEA_H
+#define CCAN_CRYPTO_XTEA_H
+/* Public domain - see LICENSE file for details */
+#include "config.h"
+#include <stdint.h>
+
+/**
+ * struct xtea_secret - secret to use for xtea encryption
+ * @u.u8: an unsigned char array.
+ * @u.u32: a 32-bit integer array.
+ * @u.u64: a 64-bit integer array.
+ *
+ * Other fields may be added to the union in future.
+ */
+struct xtea_secret {
+ union {
+ /* Array of chars */
+ unsigned char u8[16];
+ /* Array of uint32_t */
+ uint32_t u32[4];
+ /* Array of uint64_t */
+ uint64_t u64[2];
+ } u;
+};
+
+/**
+ * xtea_encipher - encrypt a 64-bit value.
+ * @secret: the xtea secret
+ * @v: the 64 bit value
+ *
+ * Returns the 64-bit encrypted value: use xtea_decipher to decrypt.
+ */
+uint64_t xtea_encipher(const struct xtea_secret *secret, uint64_t v);
+
+/**
+ * xtea_decipher - decrypt a 64-bit value.
+ * @secret: the xtea secret
+ * @e: the 64 bit encrypted value
+ *
+ * Returns the 64-bit decryptted value.
+ */
+uint64_t xtea_decipher(const struct xtea_secret *secret, uint64_t e);
+
+#endif /* CCAN_CRYPTO_SIPHASH24_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)
{
#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_); \
- 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 { \
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;
- for (ed_size i2 = 1; i2 <= slen; ++i2) {
+ for (i2 = 1; i2 <= slen; ++i2) {
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;
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;
- for (ed_size j2 = 1; j2 <= tlen; ++j2) {
+ for (j2 = 1; j2 <= tlen; ++j2) {
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;
#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;
}
/* 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]);
}
- 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;
* 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_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.
*/
/* 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]);
}
- 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]);
/* 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 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.
*/
/* 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]);
}
- 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]);
/* 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 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.
*/
/* 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]);
}
- 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];
/* 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])) {
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;
}
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? */
--- /dev/null
+../../licenses/BSD-MIT
\ No newline at end of file
--- /dev/null
+#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;
+}
+
--- /dev/null
+/* 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;
+}
+
+
+
--- /dev/null
+/* 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
+
--- /dev/null
+/* 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;
+}
+
ht->table = &ht->common_bits;
}
+/* Fill to 87.5% */
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,
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;
}
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]);
}
}
+/* 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)
{
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)
/* 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) {
- /* 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);
/* 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)
{
- 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(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);
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 *e;
+ struct htable_iter unwanted;
+
+ if (!i)
+ i = &unwanted;
+ i->off = seed % ((size_t)1 << ht->bits);
+ e = htable_next(ht, i);
+ if (!e)
+ e = htable_first(ht, i);
+ return e;
}
struct htable *htable_check(const struct htable *ht, const char *abortstr)
* 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.
htable_delval_(htable_debug(htable, HTABLE_LOC), i)
void htable_delval_(struct htable *ht, struct htable_iter *i);
+/**
+ * htable_pick - set iterator to a random valid entry.
+ * @ht: the htable
+ * @seed: a random number to use.
+ * @i: the htable_iter which is output (or NULL).
+ *
+ * Usually used with htable_delval to delete a random entry. Returns
+ * NULL iff the table is empty, otherwise a random entry.
+ */
+#define htable_pick(htable, seed, i) \
+ htable_pick_(htable_debug(htable, HTABLE_LOC), seed, i)
+void *htable_pick_(const struct htable *ht, size_t seed, struct htable_iter *i);
+
/**
* htable_set_allocator - set calloc/free functions.
* @alloc: allocator to use, must zero memory!
* 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);
*
* type *<name>_first(const struct <name> *ht, struct <name>_iter *i);
* type *<name>_next(const struct <name> *ht, struct <name>_iter *i);
* type *<name>_prev(const struct <name> *ht, struct <name>_iter *i);
- *
+ * type *<name>_pick(const struct <name> *ht, size_t seed,
+ * struct <name>_iter *i);
* It's currently safe to iterate over a changing hashtable, but you might
* miss an element. Iteration isn't very efficient, either.
*
return name##_del(ht, elem); \
return false; \
} \
+ 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) \
+ { \
+ return htable_pick(&ht->raw, seed, iter ? &iter->i : NULL); \
+ } \
static inline UNNEEDED type *name##_first(const struct name *ht, \
struct name##_iter *iter) \
{ \
--- /dev/null
+#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();
+}
int main(void)
{
- unsigned int i, weight;
+ unsigned int i;
uintptr_t perfect_bit;
struct htable ht;
uint64_t val[NUM_VALS];
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));
void *p;
struct htable_obj_iter iter;
- plan_tests(32);
+ plan_tests(35);
for (i = 0; i < NUM_VALS; i++)
val[i].key = i;
dne = i;
/* We cannot find an entry which doesn't exist. */
ok1(!htable_obj_get(&ht, &dne));
+ ok1(!htable_obj_pick(&ht, 0, NULL));
/* Fill it, it should increase in size. */
add_vals(&ht, val, NUM_VALS);
/* Find all. */
find_vals(&ht, val, NUM_VALS);
ok1(!htable_obj_get(&ht, &dne));
+ ok1(htable_obj_pick(&ht, 0, NULL));
+ ok1(htable_obj_pick(&ht, 0, &iter));
/* Walk once, should get them all. */
i = 0;
int main(void)
{
- unsigned int i, weight;
+ unsigned int i;
uintptr_t perfect_bit;
struct htable ht;
uint64_t val[NUM_VALS];
void *p;
struct htable_iter iter;
- plan_tests(38);
+ plan_tests(43);
for (i = 0; i < NUM_VALS; i++)
val[i] = i;
dne = i;
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));
+ /* htable_pick should always return that value */
+ ok1(htable_pick(&ht, 0, NULL) == val);
+ ok1(htable_pick(&ht, 1, NULL) == val);
+ ok1(htable_pick(&ht, 0, &iter) == val);
+ ok1(get_raw_ptr(&ht, ht.table[iter.off]) == val);
+
/* This should increase it again. */
add_vals(&ht, val, 1, 1);
ok1(ht.bits == 2);
htable_clear(&ht);
ok1(htable_count(&ht) == 0);
+ ok1(htable_pick(&ht, 0, NULL) == NULL);
return exit_status();
}
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)
+density: density.o hash.o $(CCAN_OBJS)
speed.o: speed.c ../htable.h ../htable.c
--- /dev/null
+/* 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;
+}
#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;
/* 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])) {
#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)
#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)
* 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));
+ * 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 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];
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.
*/
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 */
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;
/* For simplicity, we do one always at a time */
static bool handle_always(void)
{
+ int i;
+
/* 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))
* else that we manipulate events. */
static void exclude_pollfds(void)
{
+ size_t i;
+
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])
static void restore_pollfds(void)
{
+ size_t i;
+
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) {
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;
+}
/*** 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);
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] == '"'
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
+ jout->empty = true;
}
const char *json_out_contents(const struct json_out *jout, size_t *len)
* @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.
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));
}
/**
*
* 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");
+ * free(membuf_cleanup(&charbuf));
* return 0;
* }
*/
}
-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;
return -1;
}
-void net_connect_abort(struct pollfd pfds[2])
+void net_connect_abort(struct pollfd *pfds)
{
unsigned int i;
}
}
-int net_connect_complete(struct pollfd pfds[2])
+int net_connect_complete(struct pollfd *pfds)
{
unsigned int i;
* 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 -.
/**
* 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 {
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;
- opt_show_doubleval(buf, &d);
+ opt_show_doubleval(buf, len, &d);
+ return true;
}
char *opt_set_doubleval(const char *arg, double *d)
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)
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] = '"';
- 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. */
-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
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")*/
- snprintf(buf, OPT_SHOW_LEN, "0");
+ snprintf(buf, len, "0");
return;
}
for (i = 0; i < strlen(suffixes); i++){
ll = tmp;
}
if (i == 0)
- snprintf(buf, OPT_SHOW_LEN, "%"PRId64, (int64_t)ll);
+ snprintf(buf, len, "%"PRId64, (int64_t)ll);
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")*/
- snprintf(buf, OPT_SHOW_LEN, "0");
+ snprintf(buf, len, "0");
return;
}
for (i = 0; i < strlen(suffixes); i++){
ull = tmp;
}
if (i == 0)
- snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, (uint64_t)ull);
+ snprintf(buf, len, "%"PRIu64, (uint64_t)ull);
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 */
-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 */
-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 */
-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 */
-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;
}
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);
}
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);
{
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);
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;
add_opt(&opt);
}
+bool opt_unregister(const char *names)
+{
+ int found = -1, i;
+
+ for (i = 0; i < opt_count; i++) {
+ if (opt_table[i].type & OPT_SUBTABLE)
+ continue;
+ if (strcmp(opt_table[i].names, names) == 0)
+ found = i;
+ }
+ if (found == -1)
+ return false;
+ opt_count--;
+ memmove(&opt_table[found], &opt_table[found+1],
+ (opt_count - found) * sizeof(opt_table[found]));
+ return true;
+}
+
void opt_register_table(const struct opt_table entry[], const char *desc)
{
unsigned int i, start = opt_count;
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 {
* 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
_opt_register((names), OPT_CB_ARG((cb), OPT_EARLY, (show),(arg)), \
(arg), (desc))
+/**
+ * opt_unregister - unregister an option.
+ * @names: the names it was registered with.
+ *
+ * This undoes opt_register[_early]_[no]arg. Returns true if the option was
+ * found, otherwise false.
+ */
+bool opt_unregister(const char *names);
+
/**
* opt_parse - parse arguments.
* @argc: pointer to argc
*/
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);
-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);
-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);
-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);
-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);
-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);
-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);
-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);
-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);
-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
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);
/* 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. */
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 {
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;
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),
- 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 */
/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);
(*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)
{
- unsigned i, arg, len;
+ unsigned arg, len;
const char *o, *optarg = NULL;
char *problem = NULL;
+ struct opt_table *ot;
if (getenv("POSIXLY_CORRECT")) {
/* Don't find options after non-options. */
/* 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");
}
+
/* 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;
argv[arg], strlen(argv[arg]),
"unrecognized option");
}
+
+ (*offset)++;
/* 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 ((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 */
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) {
#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';
+ 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);
+ return true;
}
/* Test add_desc helper. */
char *ret;
size_t len, max;
- plan_tests(30);
+ plan_tests(32);
opt.show = NULL;
opt.names = "01234";
" (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;
int main(int argc, char *argv[])
{
- plan_tests(12);
+ plan_tests(14);
/* --aaa without args. */
opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "");
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();
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;
- 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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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;
- 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] == '!');
}
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;
- 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;
- 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;
- 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] == '!');
}
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;
- opt_show_bool(buf, &b);
+ opt_show_bool(buf, OPT_SHOW_LEN, &b);
ok1(strcmp(buf, "false") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
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;
- opt_show_invbool(buf, &b);
+ opt_show_invbool(buf, OPT_SHOW_LEN, &b);
ok1(strcmp(buf, "true") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
/* 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';
- 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] == '!');
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;
- opt_show_intval(buf, &i);
+ opt_show_intval(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
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] == '!');
}
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] == '!');
}
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] == '!');
}
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;
- opt_show_floatval(buf, &f);
+ opt_show_floatval(buf, OPT_SHOW_LEN, &f);
ok1(strcmp(buf, "77.500000") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
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;
- opt_show_doubleval(buf, &d);
+ opt_show_doubleval(buf, OPT_SHOW_LEN, &d);
ok1(strcmp(buf, "77.000000") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
static void freefn(void *ptr)
{
free_count++;
- free(ptr);
*find_ptr(ptr) = NULL;
+ free(ptr);
}
int main(int argc, char *argv[])
--- /dev/null
+#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(15);
+
+ opt_register_noarg("--aaa|-a", test_noarg, NULL, "AAAAAAll");
+ opt_register_arg("-b", test_arg, NULL, "bbb", "b");
+
+ /* We can't unregister wrong ones, but can unregister correct one */
+ ok1(!opt_unregister("--aaa"));
+ ok1(!opt_unregister("-a"));
+ ok1(opt_unregister("--aaa|-a"));
+
+ /* Arg parsing works as if we'd never registered it */
+ ok1(parse_args(&argc, &argv, "-bbbb", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(argv[1] == NULL);
+ ok1(test_cb_called == 1);
+
+ ok1(!parse_args(&argc, &argv, "--aaa", NULL));
+
+ /* We can still add another one OK. */
+ opt_register_noarg("-c", test_noarg, NULL, "AAAAAAll");
+ ok1(parse_args(&argc, &argv, "-c", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(argv[1] == NULL);
+ ok1(test_cb_called == 2);
+
+ /* parse_args allocates argv */
+ free(argv);
+ return exit_status();
+}
--- /dev/null
+#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();
+}
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;
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[];
#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;
}
}
- *start = (words[oldlen - 1] == '\n');
+ if (oldlen != 0)
+ *start = (words[oldlen - 1] == '\n');
return oldlen;
}
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;
}
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);
- 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>");
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;
return 1;
if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/closefrom\n");
printf("ccan/noerr\n");
return 0;
}
/* 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>
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(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. */
- 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);
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. */
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;
}
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)
--- /dev/null
+../../licenses/BSD-MIT
\ No newline at end of file
--- /dev/null
+#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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+#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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+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=
{
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;
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);
}
};
/* 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. */
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) {
- 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;
}
+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);
}
/* 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++) {
/**
* 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).
*
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.
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;
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 notifier {
struct prop_hdr hdr; /* NOTIFIER */
enum tal_notify_type types;
- union {
+ union notifier_cb {
void (*notifyfn)(tal_t *, enum tal_notify_type, void *);
void (*destroy)(tal_t *); /* If NOTIFY_IS_DESTRUCTOR set */
void (*destroy2)(tal_t *, void *); /* If NOTIFY_EXTRA_ARG */
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,
}
/* 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
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;
}
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;
- if (is_literal(p))
- break;
if (p->type != NOTIFIER)
continue;
n = (struct notifier *)p;
if (n->types & type) {
errno = saved_errno;
if (n->types & NOTIFY_IS_DESTRUCTOR) {
+ /* Blatt this notifier in case it tries to
+ * tal_del_destructor() from inside */
+ union notifier_cb cb = n->u;
+ /* It's a union, so this NULLs destroy2 too! */
+ n->u.destroy = NULL;
if (n->types & NOTIFY_EXTRA_ARG)
- n->u.destroy2(from_tal_hdr(ctx),
- EXTRA_ARG(n));
+ cb.destroy2(from_tal_hdr(ctx),
+ EXTRA_ARG(n));
else
- n->u.destroy(from_tal_hdr(ctx));
+ cb.destroy(from_tal_hdr(ctx));
} else
n->u.notifyfn(from_tal_hdr_or_null(ctx), type,
(void *)info);
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;
}
{
hdr->type = type;
hdr->next = parent->prop;
- parent->prop = hdr;
+ parent->prop = (char *)hdr;
}
static struct notifier *add_notifier_property(struct tal_hdr *t,
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;
- if (is_literal(*p))
+ p = is_prop_hdr(*ptr);
+ if (!p)
break;
- if ((*p)->type != NOTIFIER)
+
+ if (p->type != NOTIFIER)
continue;
- n = (struct notifier *)*p;
+ n = (struct notifier *)p;
if (n->u.notifyfn != fn)
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;
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)));
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;
- 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);
}
/* 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);
}
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 */
- 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]) {
- struct prop_hdr **p;
+ char **ptr;
+ struct prop_hdr *prop;
/* 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;
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;
+ 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)
}
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;
+ if (nullok && p == NULL) {
+ /* take(NULL) works. */
+ (void)taken(p);
+ return NULL;
+ }
+
if (!adjust_size(&nbytes, n)) {
if (taken(p))
tal_free(p);
}
ret = tal_alloc_arr_(ctx, size, n + extra, false, label);
- if (ret)
+ if (ret && p)
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 *),
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++)
- 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;
- if (is_literal(p)) {
- printf(" \"%s\"", (const char *)p);
+ prop = is_prop_hdr(ptr);
+ if (!prop) {
+ fprintf(stderr, " \"%s\"", ptr);
break;
}
- switch (p->type) {
+ switch (prop->type) {
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:
- 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:
- 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:
- 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)
dump_node(level, t);
- children = find_property(t, CHILDREN);
+ children = find_property((struct tal_hdr *)t, CHILDREN);
if (children) {
struct tal_hdr *i;
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;
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 (!in_bounds(p))
+ if (!in_bounds(prop))
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");
- children = (struct children *)p;
+ children = (struct children *)prop;
break;
case NOTIFIER:
break;
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");
/**
* 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
- * 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'. */
* @function: the function to call before it's freed.
*
* If @function has not been successfully added as a destructor, this returns
- * false.
+ * false. Note that if we're inside the destructor call itself, this will
+ * return false.
*/
#define tal_del_destructor(ptr, function) \
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
* @function: the function to call before it's freed.
*
* If @function has not been successfully added as a destructor, this returns
- * false.
+ * false. Note that if we're inside the destructor call itself, this will
+ * return false.
*/
#define tal_del_destructor(ptr, function) \
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
* 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) \
- 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_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))
((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 *), \
- 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 *), \
- sizeof(type), (n), (extra), \
+ sizeof(type), (n), (extra), false, \
label))
/**
#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.
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);
destroy_count++;
}
+static void remove_own_destructor(char *p)
+{
+ destroy_count++;
+ ok1(!tal_del_destructor(p, remove_own_destructor));
+}
+
int main(void)
{
char *child2;
- plan_tests(18);
+ plan_tests(21);
destroy_count = 0;
parent = tal(NULL, char);
tal_free(parent);
ok1(destroy_count == 4);
+ destroy_count = 0;
+ parent = tal(NULL, char);
+ ok1(tal_add_destructor(parent, remove_own_destructor));
+ tal_free(parent);
+ ok1(destroy_count == 1);
+
tal_cleanup();
return exit_status();
}
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;
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 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? */
if (bits == 0) {
return false;
}
+ if (bits >= SIZET_BITS) {
+ return true;
+ }
return ((num << bits) >> 1) != (num << (bits - 1));
}
/* 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. */
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:
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.
*
* 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 */
* 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 *)
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
{
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
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
{
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
list_node_init(&t->list);
}
-static bool list_node_initted(const struct list_node *n)
+static inline bool list_node_initted(const struct list_node *n)
{
return n->prev == n;
}
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;
-#endif
+
if (t->time < timers->first)
timers->first = t->time;
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))
--- /dev/null
+../../licenses/BSD-MIT
\ No newline at end of file
--- /dev/null
+#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;
+}
--- /dev/null
+#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();
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
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);
}
int
main(int argc, char **argv)
{
- plan_tests(2190906);
+ plan_tests(2190906 - 1);
test_unicode_scalar_value();
test_surrogates();
test_non_shortest_form();
/* 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;
* 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.
*
*/
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;
}
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");
- 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;
};
/* 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[];
{ "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; }" },
"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;" },
"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" },
"#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"
" 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, ...)
#define MAIN_BODY_BOILERPLATE "return 0;\n"
#define MAIN_END_BOILERPLATE "}\n"
-static bool run_test(const char *cmd, struct test *test)
+static bool run_test(const char *cmd, const char *wrapper, struct test *test)
{
char *output, *newcmd;
FILE *outf;
dep++;
positive = false;
}
- if (run_test(cmd, find_test(dep)) != positive) {
+ if (run_test(cmd, wrapper, find_test(dep)) != positive) {
test->answer = false;
test->done = true;
return test->answer;
/* We run INSIDE_MAIN tests for sanity checking. */
if (strstr(test->style, "EXECUTE")
|| strstr(test->style, "INSIDE_MAIN")) {
- output = run("." DIR_SEP OUTPUT_FILE, &status);
+ char *runcmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1);
+
+ 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",
= { "", DEFAULT_COMPILER, DEFAULT_FLAGS, NULL };
const char *outflag = DEFAULT_OUTPUT_EXE_FLAG;
const char *configurator_cc = NULL;
+ const char *wrapper = "";
const char *orig_cc;
const char *varfile = NULL;
const char *headerfile = NULL;
while (argc > 1) {
if (strcmp(argv[1], "--help") == 0) {
- printf("Usage: configurator [-v] [--var-file=<filename>] [-O<outflag>] [--configurator-cc=<compiler-for-tests>] [--autotools-style] [--extra-tests] [<compiler> <flags>...]\n"
+ printf("Usage: configurator [-v] [--var-file=<filename>] [-O<outflag>] [--configurator-cc=<compiler-for-tests>] [--wrapper=<wrapper-for-tests>] [--autotools-style] [--extra-tests] [<compiler> <flags>...]\n"
" <compiler> <flags> will have \"<outflag> <outfile> <infile.c>\" appended\n"
"Default: %s %s %s\n",
DEFAULT_COMPILER, DEFAULT_FLAGS,
configurator_cc = argv[1] + 18;
argc--;
argv++;
+ } else if (strncmp(argv[1], "--wrapper=", 10) == 0) {
+ wrapper = argv[1] + 10;
+ argc--;
+ argv++;
} else if (strncmp(argv[1], "--var-file=", 11) == 0) {
varfile = argv[1] + 11;
argc--;
end_test(1);
}
for (i = 0; tests[i].name; i++)
- run_test(cmd, &tests[i]);
+ run_test(cmd, wrapper, &tests[i]);
free(cmd);
remove(OUTPUT_FILE);
char *config_header;
config_header = grab_file(NULL, fname);
- tal_free(fname);
- if (!config_header)
+ if (!config_header) {
+ tal_free(fname);
return NULL;
+ }
lines = tal_strsplit(config_header, config_header, "\n", STR_EMPTY_OK);
for (i = 0; i < tal_count(lines) - 1; i++) {
fname, cflags);
}
}
+ tal_free(fname);
return config_header;
}
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>
</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">
<th align="right">Download</th>
<?php
-$modules = array_slice($argv, 4);
+$modules = array_slice($argv, 5);
sort($modules);
foreach ($modules as $module) {
<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>