# 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
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])
* @note sets errno = EDOM if src contains invalid characters
* @note sets errno = EINVAL if src is an invalid base64 tail
*/
-ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char *dest,
+ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3],
const char *src, size_t srclen);
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
* (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);
/*
#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 { \
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? */
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)
* 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.
size_t seed, \
struct name##_iter *iter) \
{ \
- /* Note &iter->i == NULL iff iter is NULL */ \
- return htable_pick(&ht->raw, seed, &iter->i); \
+ return htable_pick(&ht->raw, seed, iter ? &iter->i : NULL); \
} \
static inline UNNEEDED type *name##_first(const struct name *ht, \
struct name##_iter *iter) \
--- /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));
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));
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 */
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;
+}
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 -.
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;
int found = -1, i;
for (i = 0; i < opt_count; i++) {
- if (opt_table[i].type == OPT_SUBTABLE)
+ if (opt_table[i].type & OPT_SUBTABLE)
continue;
if (strcmp(opt_table[i].names, names) == 0)
found = i;
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
*/
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(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;
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=
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 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;
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++)
fprintf(stderr, " ");
fprintf(stderr, "%p len=%zu", t, t->bytelen);
- for (p = t->prop; p; p = p->next) {
+ for (ptr = t->prop; ptr; ptr = prop->next) {
struct children *c;
struct name *n;
struct notifier *no;
- if (is_literal(p)) {
- fprintf(stderr, " \"%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;
+ c = (struct children *)prop;
fprintf(stderr, " CHILDREN(%p):parent=%p,children={%p,%p}",
- p, c->parent,
+ prop, c->parent,
c->children.n.prev, c->children.n.next);
break;
case NAME:
- n = (struct name *)p;
- fprintf(stderr, " 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;
- fprintf(stderr, " NOTIFIER(%p):fn=%p", p, no->u.notifyfn);
+ no = (struct notifier *)prop;
+ fprintf(stderr, " NOTIFIER(%p):fn=%p", prop, no->u.notifyfn);
break;
default:
- fprintf(stderr, " **UNKNOWN(%p):%i**", p, p->type);
+ fprintf(stderr, " **UNKNOWN(%p):%i**", prop, prop->type);
}
}
fprintf(stderr, "\n");
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_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))
/**
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);
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:
* 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
--- /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 */
*/
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;
"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;" },
"#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"
/* We run INSIDE_MAIN tests for sanity checking. */
if (strstr(test->style, "EXECUTE")
|| strstr(test->style, "INSIDE_MAIN")) {
- char *cmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1);
+ char *runcmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1);
- strcpy(cmd, wrapper);
- strcat(cmd, " ." DIR_SEP OUTPUT_FILE);
- output = run(cmd, &status);
- free(cmd);
+ strcpy(runcmd, wrapper);
+ strcat(runcmd, " ." DIR_SEP OUTPUT_FILE);
+ output = run(runcmd, &status);
+ free(runcmd);
if (!strstr(test->style, "EXECUTE") && status != 0)
c12r_errx(EXIT_BAD_TEST,
"Test for %s failed with %i:\n%s",
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;
}