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

Reported-by: Christian Decker
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
72 files changed:
Makefile
ccan/antithread/alloc/alloc.c
ccan/antithread/alloc/tiny.c
ccan/base64/base64.c
ccan/base64/base64.h
ccan/bitops/test/run.c
ccan/bytestring/bytestring.h
ccan/crypto/hmac_sha256/hmac_sha256.c
ccan/darray/darray.h
ccan/failtest/failtest.c
ccan/htable/htable.c
ccan/htable/htable.h
ccan/htable/htable_type.h
ccan/htable/test/run-clash.c [new file with mode: 0644]
ccan/htable/test/run-debug.c
ccan/htable/test/run.c
ccan/htable/tools/Makefile
ccan/htable/tools/density.c [new file with mode: 0644]
ccan/idtree/idtree.c
ccan/ilog/ilog.h
ccan/io/fdpass/_info
ccan/io/io.c
ccan/io/io.h
ccan/io/poll.c
ccan/mem/mem.h
ccan/membuf/_info
ccan/net/net.c
ccan/objset/_info
ccan/opt/helpers.c
ccan/opt/opt.c
ccan/opt/opt.h
ccan/opt/parse.c
ccan/opt/test/run-add_desc.c
ccan/opt/test/run-correct-reporting.c
ccan/opt/test/run-helpers.c
ccan/opt/test/run-set_alloc.c
ccan/opt/test/run-userbits.c [new file with mode: 0644]
ccan/opt/test/utils.c
ccan/opt/test/utils.h
ccan/opt/usage.c
ccan/rbuf/rbuf.c
ccan/rune/LICENSE [new symlink]
ccan/rune/_info [new file with mode: 0644]
ccan/rune/coding.c [new file with mode: 0644]
ccan/rune/internal.h [new file with mode: 0644]
ccan/rune/rune.c [new file with mode: 0644]
ccan/rune/rune.h [new file with mode: 0644]
ccan/rune/test/run-alt-lexicographic-order.c [new file with mode: 0644]
ccan/rune/test/run-altern-escape.c [new file with mode: 0644]
ccan/rune/test/run.c [new file with mode: 0644]
ccan/rune/test/test_vectors.csv [new file with mode: 0644]
ccan/stringbuilder/stringbuilder.c
ccan/strmap/strmap.c
ccan/strmap/strmap.h
ccan/tal/tal.c
ccan/tal/tal.h
ccan/tal/test/run-notifier.c
ccan/tally/tally.c
ccan/tcon/tcon.h
ccan/tcon/test/compile_fail-container1.c
ccan/tcon/test/compile_fail-container1w.c
ccan/tcon/test/compile_fail-container3.c
ccan/tcon/test/compile_fail-container3w.c
ccan/ungraph/LICENSE [new symlink]
ccan/ungraph/_info [new file with mode: 0644]
ccan/ungraph/test/run.c [new file with mode: 0644]
ccan/ungraph/ungraph.c [new file with mode: 0644]
ccan/ungraph/ungraph.h [new file with mode: 0644]
ccan/version/version.h
tools/ccanlint/async.c
tools/configurator/configurator.c
tools/read_config_header.c

index d53e89f4d1ca9556e64187e4d3a1a63272b64c74..875e99f2146616ce41abb8bcdb6813ebdb50a53f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ all::
 
 # Our flags for building
 WARN_CFLAGS := -Wall -Wstrict-prototypes -Wold-style-definition -Wundef \
- -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
index 1b36aa47e21c39c4d6b869b3aaaf791f27a55244..33201ab97a5d8745806c34e3074f57ff05e6eadc 100644 (file)
@@ -728,8 +728,6 @@ void *alloc_get(void *pool, unsigned long poolsize,
        bs = &head->bs[bucket];
 
        if (!bs->page_list) {
-               struct page_header *ph;
-
                if (large_page_bucket(bucket, sp_bits))
                        bs->page_list = get_large_page(head, poolsize,
                                                       sp_bits);
index ffd17c65734f881f1ca355ed9e1a5b705dd3ff46..d84974ee02cd3ce0f4c967c239e30e6b466f3241 100644 (file)
@@ -353,7 +353,8 @@ bool tiny_alloc_check(void *pool, unsigned long poolsize)
        unsigned long arrsize = free_array_size(poolsize);
        unsigned char *arr = pool;
        unsigned long len, off, hdrlen;
-       unsigned long 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)
index b2326293a992b45d011999a5211b4fc9fa54634e..c28e0da2a496dcbdd09abf7688caf5bbc37e117d 100644 (file)
@@ -31,7 +31,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps,
        int8_t ret;
 
        ret = maps->decode_map[(unsigned char)b64letter];
-       if (ret == (char)0xff) {
+       if (ret == '\xff') {
                errno = EDOM;
                return -1;
        }
@@ -41,7 +41,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps,
 
 bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char)
 {
-       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])
index cef30d2576736ff3398fce192e3858f14b6ea71d..a899af4a357fc9718d5a1efd2d89b0d796aef291 100644 (file)
@@ -116,7 +116,7 @@ ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps,
  * @note sets errno = EDOM if src contains invalid characters
  * @note sets errno = EINVAL if src is an invalid base64 tail
  */
-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);
 
 
index 5dba932d4799af3b61134a0eb3ecd6d135e18b8e..6bb3acf5037173a328fa50b0c6b120dc310f8d82 100644 (file)
@@ -10,7 +10,7 @@ int main(void)
        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);
@@ -25,19 +25,19 @@ int main(void)
        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);
 
index bc99e7951c739e94221148e12491ea994642fbae..a0689db15b2f4457a9a15525edf08ee5ae4b55f2 100644 (file)
@@ -203,8 +203,13 @@ static inline const char *bytestring_rindex(struct bytestring haystack,
 static inline struct bytestring bytestring_bytestring(struct bytestring haystack,
                                                      struct bytestring needle)
 {
-       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
index 0392afe5c1127134e61e48d864dffe94252aeaa8..2238f9dc8fffbd4ee049ec8c965e0c11aeef37e2 100644 (file)
@@ -35,7 +35,8 @@ void hmac_sha256_init(struct hmac_sha256_ctx *ctx,
         *  (e.g., if K is of length 20 bytes and B=64, then K will be
         *   appended with 44 zero bytes 0x00)
         */
-       memcpy(k_ipad, k, ksize);
+       if (ksize != 0)
+               memcpy(k_ipad, k, ksize);
        memset((char *)k_ipad + ksize, 0, HMAC_SHA256_BLOCKSIZE - ksize);
 
        /*
index 58470fdee6c5561e49c265b1a975fa90b6b9ca26..0b98fdacddb4febe6d0d92bb5d07bfd4a454fd62 100644 (file)
@@ -183,15 +183,21 @@ typedef darray(unsigned long)  darray_ulong;
 
 #define darray_append_items(arr, items, count) do { \
                size_t count_ = (count), oldSize_ = (arr).size; \
-               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 { \
index c61ce442a5d03b39444a87a468514f23c6f3c962..205ded254709e6611d67b4465ca3967744306252 100644 (file)
@@ -179,7 +179,8 @@ static struct failtest_call *add_history_(enum failtest_call_type type,
        call->line = line;
        call->cleanup = NULL;
        call->backtrace = get_backtrace(&call->backtrace_num);
-       memcpy(&call->u, elem, elem_size);
+       if (elem_size != 0)
+               memcpy(&call->u, elem, elem_size);
        tlist_add_tail(&history, call, list);
        return call;
 }
@@ -1202,9 +1203,8 @@ static void cleanup_pipe(struct pipe_call *call, bool restore)
 int failtest_pipe(int pipefd[2], const char *file, unsigned line)
 {
        struct failtest_call *p;
-       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? */
index cffd0619d338d8146365abd3f10e1b308ef4c4d4..f631ffebf1f7540416a050c800bcf5cd42268470 100644 (file)
@@ -86,14 +86,16 @@ void htable_init(struct htable *ht,
        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,
@@ -103,7 +105,7 @@ 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;
        }
@@ -195,12 +197,83 @@ void *htable_prev_(const struct htable *ht, struct htable_iter *i)
        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)
 {
@@ -214,6 +287,8 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
                i = (i + 1) & ((1 << ht->bits)-1);
        }
        ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
+       if (!entry_is_valid(ht->table[i]))
+               update_common_fix_invalid(ht, new, h);
 }
 
 static COLD bool double_table(struct htable *ht)
@@ -283,20 +358,10 @@ static COLD void rehash_table(struct htable *ht)
 /* We stole some bits, now we need to put them back... */
 static COLD void update_common(struct htable *ht, const void *p)
 {
-       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);
@@ -306,33 +371,25 @@ static COLD void update_common(struct htable *ht, const void *p)
        /* Find bits which are unequal to old common set. */
        maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask);
 
-       /* 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);
 
@@ -361,8 +418,12 @@ void htable_delval_(struct htable *ht, struct htable_iter *i)
        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)
index eac57e37a326612226f6cd7907c04bcbc589c360..faaf541bd8ce8c5ee5f5ea64eab3649e1462fade 100644 (file)
@@ -132,7 +132,7 @@ bool htable_copy_(struct htable *dst, const struct htable *src);
  * htable_add - add a pointer into a hash table.
  * @ht: the htable
  * @hash: the hash value of the object
- * @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.
index bb5ea086b7319fd3cd1c19b0dceece1974cfe8d4..0aacb7f334925ffe58d2c8cb489a9ae0efe36b66 100644 (file)
                                                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)      \
diff --git a/ccan/htable/test/run-clash.c b/ccan/htable/test/run-clash.c
new file mode 100644 (file)
index 0000000..a4e5d38
--- /dev/null
@@ -0,0 +1,31 @@
+#include <ccan/htable/htable.h>
+#include <ccan/htable/htable.c>
+#include <ccan/tap/tap.h>
+#include <stdbool.h>
+#include <string.h>
+
+/* Clashy hash */
+static size_t hash(const void *elem, void *unused UNNEEDED)
+{
+       return 0;
+}
+
+int main(void)
+{
+       struct htable ht;
+
+       plan_tests(254 * 253);
+       /* We try to get two elements which clash */
+       for (size_t i = 2; i < 256; i++) {
+               for (size_t j = 2; j < 256; j++) {
+                       if (i == j)
+                               continue;
+                       htable_init(&ht, hash, NULL);
+                       htable_add(&ht, hash((void *)i, NULL), (void *)i);
+                       htable_add(&ht, hash((void *)j, NULL), (void *)j);
+                       ok1(htable_check(&ht, "test"));
+                       htable_clear(&ht);
+               }
+       }
+       return exit_status();
+}
index b9f48ee70aa96afc0dec95d98d8240b2659bba17..399910354da21675f8767525a763d7bc44abfb14 100644 (file)
@@ -107,7 +107,7 @@ static bool check_mask(struct htable *ht, uint64_t val[], unsigned num)
 
 int main(void)
 {
-       unsigned int i, weight;
+       unsigned int i;
        uintptr_t perfect_bit;
        struct htable ht;
        uint64_t val[NUM_VALS];
@@ -131,14 +131,7 @@ int main(void)
        add_vals(&ht, val, 0, 1);
        ok1(ht.bits == 1);
        ok1(ht_max(&ht) == 1);
-       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));
index ada85f95a93d803d3db678e292bb2e33a23666cc..27007a41ac5f94d452ac4a91121b5a122d7f87fe 100644 (file)
@@ -97,7 +97,7 @@ static bool check_mask(struct htable *ht, uint64_t val[], unsigned num)
 
 int main(void)
 {
-       unsigned int i, weight;
+       unsigned int i;
        uintptr_t perfect_bit;
        struct htable ht;
        uint64_t val[NUM_VALS];
@@ -122,14 +122,7 @@ int main(void)
        add_vals(&ht, val, 0, 1);
        ok1(ht.bits == 1);
        ok1(ht_max(&ht) == 1);
-       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));
index a2cad59f0c2dd7e2edf17fd290d89c7a0112e522..c8a428a7567ca08458bfe5be6f749ea281c563b6 100644 (file)
@@ -4,9 +4,10 @@ CFLAGS=-Wall -Werror -O3 -I$(CCANDIR)
 
 CCAN_OBJS:=ccan-tal.o ccan-tal-str.o ccan-tal-grab_file.o ccan-take.o ccan-time.o ccan-str.o ccan-noerr.o ccan-list.o
 
-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
 
diff --git a/ccan/htable/tools/density.c b/ccan/htable/tools/density.c
new file mode 100644 (file)
index 0000000..5f7400b
--- /dev/null
@@ -0,0 +1,107 @@
+/* Density measurements for hashtables. */
+#include <ccan/err/err.h>
+#include <ccan/htable/htable_type.h>
+#include <ccan/htable/htable.c>
+#include <ccan/hash/hash.h>
+#include <ccan/ptrint/ptrint.h>
+#include <ccan/time/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* We don't actually hash objects: we put values in as if they were ptrs */
+static uintptr_t key(const ptrint_t *obj)
+{
+       return ptr2int(obj);
+}
+
+static size_t hash_uintptr(uintptr_t key)
+{
+       return hashl(&key, 1, 0);
+}
+
+static bool cmp(const ptrint_t *p, uintptr_t k)
+{
+       return key(p) == k;
+}
+
+HTABLE_DEFINE_TYPE(ptrint_t, key, hash_uintptr, cmp, htable_ptrint);
+
+/* Nanoseconds per operation */
+static size_t normalize(const struct timeabs *start,
+                       const struct timeabs *stop,
+                       unsigned int num)
+{
+       return time_to_nsec(time_divide(time_between(*stop, *start), num));
+}
+
+static size_t average_run(const struct htable_ptrint *ht, size_t count, size_t *longest)
+{
+       size_t i, total = 0;
+
+       *longest = 0;
+       for (i = 0; i < count; i++) {
+               size_t h = hash_uintptr(i + 2);
+               size_t run = 1;
+
+               while (get_raw_ptr(&ht->raw, ht->raw.table[h % ((size_t)1 << ht->raw.bits)]) != int2ptr(i + 2)) {
+                       h++;
+                       run++;
+               }
+               total += run;
+               if (run > *longest)
+                       *longest = run;
+       }
+       return total / count;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int i;
+       size_t num;
+       struct timeabs start, stop;
+       struct htable_ptrint ht;
+
+       if (argc != 2)
+               errx(1, "Usage: density <power-of-2-tablesize>");
+
+       num = atoi(argv[1]);
+
+       printf("Total buckets, buckets used, nanoseconds search time per element, avg run, longest run\n");
+       for (i = 1; i <= 99; i++) {
+               uintptr_t j;
+               struct htable_ptrint_iter it;
+               size_t count, avg_run, longest_run;
+               ptrint_t *p;
+
+               htable_ptrint_init_sized(&ht, num * 3 / 4);
+               assert((1 << ht.raw.bits) == num);
+
+               /* Can't put 0 or 1 in the hash table: multiply by a prime. */
+               for (j = 0; j < num * i / 100; j++) {
+                       htable_ptrint_add(&ht, int2ptr(j + 2));
+                       /* stop it from doubling! */
+                       ht.raw.elems = num / 2;
+               }
+               /* Must not have changed! */
+               assert((1 << ht.raw.bits) == num);
+
+               /* Clean cache */
+               count = 0;
+               for (p = htable_ptrint_first(&ht, &it); p; p = htable_ptrint_next(&ht, &it))
+                       count++;
+               assert(count == num * i / 100);
+               start = time_now();
+               for (j = 0; j < count; j++)
+                       assert(htable_ptrint_get(&ht, j + 2));
+               stop = time_now();
+               avg_run = average_run(&ht, count, &longest_run);
+               printf("%zu,%zu,%zu,%zu,%zu\n",
+                      num, count, normalize(&start, &stop, count), avg_run, longest_run);
+               fflush(stdout);
+               htable_ptrint_clear(&ht);
+       }
+
+       return 0;
+}
index 6e75450e67e85af287a9967cd7e88af66e147a5b..e10c17239cdfc407e40da7c77dfb4e5f57edc42a 100644 (file)
@@ -42,9 +42,9 @@
 #define MAX_LEVEL (MAX_ID_SHIFT + IDTREE_BITS - 1) / IDTREE_BITS
 #define IDTREE_FREE_MAX MAX_LEVEL + MAX_LEVEL
 
-#define 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;
@@ -122,7 +122,7 @@ restart:
                        /* 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])) {
index 9adbb8243f6c4f67da7ccefbc8fc7735f63cda7e..32702b178567e503c11d606d655738fe9dbfc3aa 100644 (file)
@@ -120,7 +120,10 @@ int ilog64_nz(uint64_t _v) CONST_FUNCTION;
 #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)
@@ -128,7 +131,7 @@ int ilog64_nz(uint64_t _v) CONST_FUNCTION;
 #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)
index ba09025aaf8e5bed001debd89a018f9385415910..0b10e8a8bbf489bb2ddcf6223d27ece866240610 100644 (file)
  *                                     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. 
index 36dcb81e720f448bf46665f93bb40f0692d2256c..12df06d82cb6474fed825cd381f7f225fc6213b1 100644 (file)
@@ -529,6 +529,18 @@ bool io_plan_out_started(const struct io_conn *conn)
        return conn->plan[IO_OUT].status == IO_POLLING_STARTED;
 }
 
+/* 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];
index e6905fb9241a78e10b56a5b63b432a99597ee185..eeb5e36ecdff0bfedd633b734cc2b380b92d8b2b 100644 (file)
@@ -389,6 +389,39 @@ struct io_plan *io_out_always_(struct io_conn *conn,
                                                       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.
@@ -781,4 +814,13 @@ struct timemono (*io_time_override(struct timemono (*now)(void)))(void);
  */
 int (*io_poll_override(int (*poll)(struct pollfd *fds, nfds_t nfds, int timeout)))(struct pollfd *, nfds_t, int);
 
+/**
+ * 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 */
index 4cc9f4b7dc901c36ae4bf0869c0d1ebc9cee6c52..634f83d286a66669c54694daf2fab0de4ce1a416 100644 (file)
@@ -464,3 +464,15 @@ void *io_loop(struct timers *timers, struct timer **expired)
 
        return ret;
 }
+
+const void *io_have_fd(int fd, bool *listener)
+{
+       for (size_t i = 0; i < num_fds; i++) {
+               if (fds[i]->fd != fd)
+                       continue;
+               if (listener)
+                       *listener = fds[i]->listener;
+               return fds[i];
+       }
+       return NULL;
+}
index 19f69c038c67efe1e11ac8ab5cec036123aa3b95..20286dcbefd42f99a7e88932ab1ef4cda115f2e2 100644 (file)
@@ -104,7 +104,7 @@ void *memcchr(void const *data, int c, size_t data_len);
 PURE_FUNCTION
 static inline bool memeq(const void *a, size_t al, const void *b, size_t bl)
 {
-       return al == bl && !memcmp(a, b, bl);
+       return al == bl && (al == 0 || !memcmp(a, b, bl));
 }
 
 /**
index bdcbce2b2f205724d39a6783e212eee2da3a7e83..a859318c62ee3486f5110ad9dbe7ee74e9e08438 100644 (file)
  *
  *     membuf_init(&charbuf, malloc(10), 10, membuf_realloc);
  *
- *     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;
  * }
  */
index 11c6b670aa456809f528c0eb6d905d2cb3c0b5bb..f57b8dd6ea1b4becf24763b351ea093586635a56 100644 (file)
@@ -75,7 +75,7 @@ close:
 }
 
 
-int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
+int net_connect_async(const struct addrinfo *addrinfo, struct pollfd *pfds)
 {
        const struct addrinfo *addr[2] = { NULL, NULL };
        unsigned int i;
@@ -122,7 +122,7 @@ int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
        return -1;
 }
 
-void net_connect_abort(struct pollfd pfds[2])
+void net_connect_abort(struct pollfd *pfds)
 {
        unsigned int i;
 
@@ -133,7 +133,7 @@ void net_connect_abort(struct pollfd pfds[2])
        }
 }
 
-int net_connect_complete(struct pollfd pfds[2])
+int net_connect_complete(struct pollfd *pfds)
 {
        unsigned int i;
 
index 967764e7e86cb9de97c622e9b297e74071b85907..116c2596ee622308cabc0f2268b9f74346321fa5 100644 (file)
@@ -39,6 +39,8 @@
  *                                     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 -.
index 118e543602e06b981e6faee7dd5f416cf819d5cd..df7ee6bb1f20cf791fe82525393e7c809b6f7afb 100644 (file)
@@ -138,10 +138,11 @@ char *opt_set_floatval(const char *arg, float *f)
        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)
@@ -160,9 +161,10 @@ 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)
@@ -196,52 +198,60 @@ char *opt_usage_and_exit(const char *extra)
        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
@@ -447,14 +457,14 @@ char * opt_set_uintval_si(const char *arg, unsigned int *u)
   are separate but essentially identical functions for signed and unsigned
   values, so that unsigned values greater than LLONG_MAX get suffixes.
  */
-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++){
@@ -464,19 +474,20 @@ static void show_llong_with_suffix(char buf[OPT_SHOW_LEN], long long ll,
                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++){
@@ -486,72 +497,84 @@ static void show_ullong_with_suffix(char buf[OPT_SHOW_LEN], unsigned long long u
                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;
 }
 
index d376a598da932445de592c8f12e2466d4bc431bd..9149374cb001e6152eb46e03de6cc092ef6e4297 100644 (file)
@@ -34,7 +34,7 @@ static const char *next_name(const char *names, unsigned *len)
 static const char *first_opt(unsigned *i, unsigned *len)
 {
        for (*i = 0; *i < opt_count; (*i)++) {
-               if (opt_table[*i].type == OPT_SUBTABLE)
+               if (opt_table[*i].type & OPT_SUBTABLE)
                        continue;
                return first_name(opt_table[*i].names, len);
        }
@@ -44,7 +44,7 @@ static const char *first_opt(unsigned *i, unsigned *len)
 static const char *next_opt(const char *p, unsigned *i, unsigned *len)
 {
        for (; *i < opt_count; (*i)++) {
-               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);
@@ -114,10 +114,11 @@ static void check_opt(const struct opt_table *entry)
 {
        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);
 
@@ -161,7 +162,7 @@ static void add_opt(const struct opt_table *entry)
 void _opt_register(const char *names, enum opt_type type,
                   char *(*cb)(void *arg),
                   char *(*cb_arg)(const char *optarg, void *arg),
-                  void (*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;
@@ -181,7 +182,7 @@ bool opt_unregister(const char *names)
        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;
@@ -203,7 +204,7 @@ void opt_register_table(const struct opt_table entry[], const char *desc)
                add_opt(&heading);
        }
        for (i = 0; entry[i].type != OPT_END; i++) {
-               if (entry[i].type == OPT_SUBTABLE)
+               if (entry[i].type & OPT_SUBTABLE)
                        opt_register_table(subtable_of(&entry[i]),
                                           entry[i].desc);
                else {
index 6f4b9dda8c85a4f33389863963b57f1a87214ea4..e0331be264230cb57a100f30e26a6f119bf6ca6c 100644 (file)
@@ -47,10 +47,11 @@ struct opt_table;
  * where "type" is the type of the @arg argument.  The first argument to the
  * @cb is the argument found on the commandline.
  *
- * 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
@@ -429,40 +430,38 @@ void opt_usage_exit_fail(const char *msg, ...) NORETURN;
  */
 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
@@ -482,19 +481,19 @@ char *opt_set_ulonglongval_bi(const char *arg, unsigned long long *ll);
 char *opt_set_ulonglongval_si(const char *arg, unsigned long long *ll);
 
 
-void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x);
-void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x);
-void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x);
-void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x);
-void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x);
-void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x);
+bool opt_show_intval_bi(char *buf, size_t len, const int *x);
+bool opt_show_longval_bi(char *buf, size_t len, const long *x);
+bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x);
+bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x);
+bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x);
+bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x);
 
-void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x);
-void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x);
-void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x);
-void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x);
-void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x);
-void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x);
+bool opt_show_intval_si(char *buf, size_t len, const int *x);
+bool opt_show_longval_si(char *buf, size_t len, const long *x);
+bool opt_show_longlongval_si(char *buf, size_t len, const long long *x);
+bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x);
+bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x);
+bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x);
 
 
 
@@ -509,6 +508,30 @@ char *opt_version_and_exit(const char *version);
 /* Display usage string to stdout, exit(0). */
 char *opt_usage_and_exit(const char *extra);
 
+/**
+ * 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. */
@@ -518,6 +541,11 @@ enum opt_type {
        OPT_SUBTABLE = 4,       /* Actually, longopt points to a subtable... */
        OPT_EARLY = 8,          /* Parse this from opt_early_parse() only. */
        OPT_END = 16,           /* End of the table. */
+
+       /* 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 {
@@ -525,7 +553,7 @@ struct opt_table {
        enum opt_type type;
        char *(*cb)(void *arg); /* OPT_NOARG */
        char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */
-       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;
@@ -551,14 +579,14 @@ struct opt_table {
                          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 */
index d227f7bca2ab679033dd920fe426983dc0ea9314..b932bf333571620d0e19edca1b10a82cc70d8397 100644 (file)
@@ -14,7 +14,8 @@
 /tmp/opt-example: option requires an argument -- 's'
 */
 static int parse_err(void (*errlog)(const char *fmt, ...),
-                    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);
@@ -28,13 +29,63 @@ static void consume_option(int *argc, char *argv[], unsigned optnum)
        (*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. */
@@ -58,34 +109,22 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
        /* Long options start with -- */
        if (argv[arg][1] == '-') {
                assert(*offset == 0);
-               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;
@@ -94,17 +133,19 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
                                         argv[arg], strlen(argv[arg]),
                                         "unrecognized option");
                }
+
+               (*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 */
@@ -117,9 +158,8 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
                if (!optarg)
                        return parse_err(errlog, argv[0], o, len,
                                         "requires an argument");
-               if ((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) {
index b559c7f747689bae70d34589ee9794f4cf710ac0..03e6986dd191954c7f14491353db1adfe0e51d51 100644 (file)
@@ -4,15 +4,24 @@
 #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. */
@@ -22,7 +31,7 @@ int main(void)
        char *ret;
        size_t len, max;
 
-       plan_tests(30);
+       plan_tests(32);
 
        opt.show = NULL;
        opt.names = "01234";
@@ -113,6 +122,14 @@ int main(void)
                   "        (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;
index 8534f291ac3e9accfeafb0685fc61286b6adf4b6..0c4f6c8693972b489440feddd1a38b260a8011f9 100644 (file)
@@ -10,7 +10,7 @@
 
 int main(int argc, char *argv[])
 {
-       plan_tests(12);
+       plan_tests(14);
 
        /* --aaa without args. */
        opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "");
@@ -42,6 +42,10 @@ int main(int argc, char *argv[])
        free(err_output);
        err_output = NULL;
 
+       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();
index 0a08a85f7aa3930011cba5600186f3a21d5c5f09..9aa41fe8db62c5f384b517a811b9b49b3ca3577d 100644 (file)
@@ -476,26 +476,26 @@ int main(int argc, char *argv[])
                        char buf[OPT_SHOW_LEN+2] = { 0 };
                        buf[OPT_SHOW_LEN] = '!';
                        i = -77;
-                       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] == '!');
                }
@@ -506,27 +506,27 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -537,23 +537,23 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -564,19 +564,19 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -587,23 +587,23 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -614,19 +614,19 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -860,26 +860,26 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -890,27 +890,27 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -921,23 +921,23 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -948,19 +948,19 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -971,23 +971,23 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -998,19 +998,19 @@ int main(int argc, char *argv[])
                        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] == '!');
                }
@@ -1090,12 +1090,12 @@ int main(int argc, char *argv[])
                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] == '!');
        }
@@ -1107,12 +1107,12 @@ int main(int argc, char *argv[])
                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] == '!');
        }
@@ -1126,14 +1126,14 @@ int main(int argc, char *argv[])
                /* 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] == '!');
@@ -1147,12 +1147,12 @@ int main(int argc, char *argv[])
                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] == '!');
        }
@@ -1164,7 +1164,7 @@ int main(int argc, char *argv[])
                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] == '!');
        }
@@ -1176,7 +1176,7 @@ int main(int argc, char *argv[])
                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] == '!');
        }
@@ -1188,7 +1188,7 @@ int main(int argc, char *argv[])
                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] == '!');
        }
@@ -1200,12 +1200,12 @@ int main(int argc, char *argv[])
                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] == '!');
        }
@@ -1217,12 +1217,12 @@ int main(int argc, char *argv[])
                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] == '!');
        }
index 1dbb351bedf41b0022a8abd26b3c0fbbc07ad445..2d7410ae22853e6bb9fbe6c3896c58dc01773a5c 100644 (file)
@@ -59,8 +59,8 @@ static void *reallocfn(void *ptr, size_t size)
 static void freefn(void *ptr)
 {
        free_count++;
-       free(ptr);
        *find_ptr(ptr) = NULL;
+       free(ptr);
 }
 
 int main(int argc, char *argv[])
diff --git a/ccan/opt/test/run-userbits.c b/ccan/opt/test/run-userbits.c
new file mode 100644 (file)
index 0000000..7f102f0
--- /dev/null
@@ -0,0 +1,59 @@
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+int main(int argc, char *argv[])
+{
+       const char *myname = argv[0];
+
+       plan_tests(28);
+
+       opt_register_noarg("-a", test_noarg, NULL, "All");
+       opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll");
+       opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "AAAAAAll");
+
+       ok1(strcmp(opt_table[0].names, "-a") == 0);
+       ok1(opt_table[0].type == OPT_NOARG);
+       ok1(strcmp(opt_table[1].names, "--aaa") == 0);
+       ok1(opt_table[1].type == OPT_NOARG);
+       ok1(strcmp(opt_table[2].names, "-b|--bbb") == 0);
+       ok1(opt_table[2].type == OPT_HASARG);
+
+       opt_table[0].type |= (1 << OPT_USER_START);
+       opt_table[1].type |= ((1 << OPT_USER_END)-1) - ((1 << OPT_USER_START)-1);
+       opt_table[2].type |= (1 << OPT_USER_END);
+
+       /* Should all work fine! */
+       ok1(parse_args(&argc, &argv, "-a", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(test_cb_called == 1);
+
+       ok1(parse_args(&argc, &argv, "--aaa", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(test_cb_called == 2);
+
+       /* This one needs an arg. */
+       ok1(parse_args(&argc, &argv, "-b", NULL) == false);
+       ok1(test_cb_called == 2);
+       ok1(parse_args(&argc, &argv, "-b", "bbb", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 3);
+
+       ok1(parse_args(&argc, &argv, "--bbb", "bbb", NULL));
+       ok1(argc == 1);
+       ok1(argv[0] == myname);
+       ok1(argv[1] == NULL);
+       ok1(test_cb_called == 4);
+
+       /* parse_args allocates argv */
+       free(argv);
+       return exit_status();
+}
index 2ff04884ebdc0efe6dc4d9dd8fe4ae6af4b9175b..61199fb4676dbdeaa8a5cdf81a6fba7a27d5de5d 100644 (file)
@@ -21,9 +21,10 @@ char *test_arg(const char *optarg, const char *arg)
        return NULL;
 }
 
-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;
index 12cf0b753e99be441ead57f73b1dc1c3e2d79999..3ada62d117c048ef666f5078d874c5bcc6a47129 100644 (file)
@@ -13,7 +13,7 @@ void reset_options(void);
 extern unsigned int test_cb_called;
 char *test_noarg(void *arg);
 char *test_arg(const char *optarg, const char *arg);
-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[];
index 12f44a48752e0e0d4e0d722c74b7513561334733..568e4661d8098bf1fd009a169867b0d369339fb2 100644 (file)
@@ -20,6 +20,9 @@ const char opt_hidden[1];
 #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;
@@ -72,7 +75,8 @@ static size_t consume_words(const char *words, size_t maxlen, size_t *prefix,
                }
        }
 
-       *start = (words[oldlen - 1] == '\n');
+       if (oldlen != 0)
+               *start = (words[oldlen - 1] == '\n');
        return oldlen;
 }
 
@@ -147,20 +151,20 @@ static char *add_desc(char *base, size_t *len, size_t *max,
        if (opt->show) {
                char buf[OPT_SHOW_LEN + sizeof("...")];
                strcpy(buf + OPT_SHOW_LEN, "...");
-               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;
 }
@@ -181,10 +185,10 @@ char *opt_usage(const char *argv0, const char *extra)
                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>");
@@ -220,7 +224,7 @@ char *opt_usage(const char *argv0, const char *extra)
        for (i = 0; i < opt_count; i++) {
                if (opt_table[i].desc == opt_hidden)
                        continue;
-               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;
index d8d658d37a3923454324dfe06a3d4bac777b91d0..cc10cf3d7f25e25531d58a9c4b07c8500ff93bf7 100644 (file)
@@ -74,9 +74,11 @@ char *rbuf_read_str(struct rbuf *rbuf, char term)
        ssize_t r = 0;
        size_t prev = 0;
 
-       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)
diff --git a/ccan/rune/LICENSE b/ccan/rune/LICENSE
new file mode 120000 (symlink)
index 0000000..2354d12
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/rune/_info b/ccan/rune/_info
new file mode 100644 (file)
index 0000000..2b2e2e8
--- /dev/null
@@ -0,0 +1,130 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * rune - Simple cookies you can extend (a-la Python runes class).
+ *
+ * This code is a form of cookies, but they are user-extensible, and
+ * contain a simple language to define what the cookie allows.
+ *
+ * A "rune" contains the hash of a secret (so the server can
+ * validate), such that you can add, but not subtract, conditions.
+ * This is a simplified form of Macaroons, See
+ * https://research.google/pubs/pub41892/ "Macaroons: Cookies with
+ * Contextual Caveats for Decentralized Authorization in the Cloud".
+ * It has one good idea, some extended ideas nobody implements, and
+ * lots and lots of words.
+ *
+ * License: BSD-MIT
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ * Example:
+ * // Given "generate secret 1" outputs kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ==
+ * // Given "add kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ== uid=rusty" outputs Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk=
+ * // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= rusty" outputs PASSED
+ * // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= notrusty" outputs FAILED: uid is not equal to rusty
+ * // Given "add Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= t\<1655958616" outputs _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2
+ * // Given "test secret _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2 rusty" outputs FAILED: t is greater or equal to 1655958616
+ * #include <ccan/err/err.h>
+ * #include <ccan/rune/rune.h>
+ * #include <ccan/str/str.h>
+ * #include <stdio.h>
+ * #include <sys/time.h>
+ * 
+ * // We support two values: current time (t), and user id (uid).
+ * static const char *check(const tal_t *ctx,
+ *                         const struct rune *rune,
+ *                         const struct rune_altern *alt,
+ *                         char *uid)
+ * {
+ *     // t= means current time, in seconds, as integer
+ *     if (streq(alt->fieldname, "t")) {
+ *             struct timeval now;
+ *             gettimeofday(&now, NULL);
+ *             return rune_alt_single_int(ctx, alt, now.tv_sec);
+ *     }
+ *     if (streq(alt->fieldname, "uid")) {
+ *             return rune_alt_single_str(ctx, alt, uid, strlen(uid));
+ *     }
+ *     // Otherwise, field is missing
+ *     return rune_alt_single_missing(ctx, alt);
+ * }
+ * 
+ * int main(int argc, char *argv[])
+ * {
+ *     struct rune *master, *rune;
+ * 
+ *     if (argc < 3)
+ *             goto usage;
+ * 
+ *     if (streq(argv[1], "generate")) {
+ *             // Make master, derive a unique_id'd rune.
+ *             if (argc != 3 && argc != 4)
+ *                     goto usage;
+ *             master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL);
+ *             rune = rune_derive_start(NULL, master, argv[3]);
+ *     } else if (streq(argv[1], "add")) {
+ *             // Add a restriction
+ *             struct rune_restr *restr;
+ *             if (argc != 4)
+ *                     goto usage;
+ *             rune = rune_from_base64(NULL, argv[2]);
+ *             if (!rune)
+ *                     errx(1, "Bad rune");
+ *             restr = rune_restr_from_string(NULL, argv[3], strlen(argv[3]));
+ *             if (!restr)
+ *                     errx(1, "Bad restriction string");
+ *             rune_add_restr(rune, restr);
+ *     } else if (streq(argv[1], "test")) {
+ *             const char *err;
+ *             if (argc != 5)
+ *                     goto usage;
+ *             master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL);
+ *             if (!master)
+ *                     errx(1, "Bad master rune");
+ *             rune = rune_from_base64(NULL, argv[3]);
+ *             if (!rune)
+ *                     errx(1, "Bad rune");
+ *             err = rune_test(NULL, master, rune, check, argv[4]);
+ *             if (err)
+ *                     printf("FAILED: %s\n", err);
+ *             else
+ *                     printf("PASSED\n");
+ *             return 0;
+ *     } else
+ *             goto usage;
+ * 
+ *     printf("%s\n", rune_to_base64(NULL, rune));
+ *     return 0;
+ * 
+ * usage:
+ *     errx(1, "Usage: %s generate <secret> <uniqueid> OR\n"
+ *          "%s add <rune> <restriction> OR\n"
+ *          "%s test <secret> <rune> <uid>", argv[0], argv[0], argv[0]);
+ * }
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/base64\n");
+               printf("ccan/crypto/sha256\n");
+               printf("ccan/endian\n");
+               printf("ccan/mem\n");
+               printf("ccan/short_types\n");
+               printf("ccan/str/hex\n");
+               printf("ccan/tal/str\n");
+               printf("ccan/tal\n");
+               printf("ccan/typesafe_cb\n");
+               return 0;
+       }
+       if (strcmp(argv[1], "testdepends") == 0) {
+               printf("ccan/tal/grab_file\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/rune/coding.c b/ccan/rune/coding.c
new file mode 100644 (file)
index 0000000..495d37c
--- /dev/null
@@ -0,0 +1,426 @@
+/* MIT (BSD) license - see LICENSE file for details */
+/* Routines to encode / decode a rune */
+#include <ccan/rune/rune.h>
+#include <ccan/rune/internal.h>
+#include <ccan/str/hex/hex.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/base64/base64.h>
+#include <ccan/endian/endian.h>
+#include <errno.h>
+
+/* From Python base64.urlsafe_b64encode:
+ *
+ * The alphabet uses '-' instead of '+' and '_' instead of '/'.
+ */
+static const base64_maps_t base64_maps_urlsafe = {
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
+
+  "\xff\xff\xff\xff\xff" /* 0 */
+  "\xff\xff\xff\xff\xff" /* 5 */
+  "\xff\xff\xff\xff\xff" /* 10 */
+  "\xff\xff\xff\xff\xff" /* 15 */
+  "\xff\xff\xff\xff\xff" /* 20 */
+  "\xff\xff\xff\xff\xff" /* 25 */
+  "\xff\xff\xff\xff\xff" /* 30 */
+  "\xff\xff\xff\xff\xff" /* 35 */
+  "\xff\xff\xff\xff\xff" /* 40 */
+  "\x3e\xff\xff\x34\x35" /* 45 */
+  "\x36\x37\x38\x39\x3a" /* 50 */
+  "\x3b\x3c\x3d\xff\xff" /* 55 */
+  "\xff\xff\xff\xff\xff" /* 60 */
+  "\x00\x01\x02\x03\x04" /* 65 A */
+  "\x05\x06\x07\x08\x09" /* 70 */
+  "\x0a\x0b\x0c\x0d\x0e" /* 75 */
+  "\x0f\x10\x11\x12\x13" /* 80 */
+  "\x14\x15\x16\x17\x18" /* 85 */
+  "\x19\xff\xff\xff\xff" /* 90 */
+  "\x3f\xff\x1a\x1b\x1c" /* 95 */
+  "\x1d\x1e\x1f\x20\x21" /* 100 */
+  "\x22\x23\x24\x25\x26" /* 105 */
+  "\x27\x28\x29\x2a\x2b" /* 110 */
+  "\x2c\x2d\x2e\x2f\x30" /* 115 */
+  "\x31\x32\x33\xff\xff" /* 120 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 135 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 145 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 165 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 175 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 195 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 205 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 225 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 235 */
+  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */
+};
+
+/* For encoding as a string */
+struct wbuf {
+       size_t off, len;
+       char *buf;
+};
+
+static void to_wbuf(const char *s, size_t len, void *vwbuf)
+{
+       struct wbuf *wbuf = vwbuf;
+
+       while (wbuf->off + len > wbuf->len)
+               tal_resize(&wbuf->buf, wbuf->len *= 2);
+       memcpy(wbuf->buf + wbuf->off, s, len);
+       wbuf->off += len;
+}
+
+/* For adding to sha256 */
+static void to_sha256(const char *s, size_t len, void *vshactx)
+{
+       struct sha256_ctx *shactx = vshactx;
+       sha256_update(shactx, s, len);
+}
+
+static void rune_altern_encode(const struct rune_altern *altern,
+                              void (*cb)(const char *s, size_t len,
+                                         void *arg),
+                              void *arg)
+{
+       char cond = altern->condition;
+       const char *p;
+
+       cb(altern->fieldname, strlen(altern->fieldname), arg);
+       cb(&cond, 1, arg);
+
+       p = altern->value;
+       for (;;) {
+               char esc[2] = { '\\' };
+               size_t len = strcspn(p, "\\|&");
+               cb(p, len, arg);
+               if (!p[len])
+                       break;
+               esc[1] = p[len];
+               cb(esc, 2, arg);
+               p += len + 1;
+       }
+}
+
+static void rune_restr_encode(const struct rune_restr *restr,
+                             void (*cb)(const char *s, size_t len,
+                                        void *arg),
+                             void *arg)
+{
+       for (size_t i = 0; i < tal_count(restr->alterns); i++) {
+               if (i != 0)
+                       cb("|", 1, arg);
+               rune_altern_encode(restr->alterns[i], cb, arg);
+       }
+}
+
+void rune_sha256_add_restr(struct sha256_ctx *shactx,
+                          struct rune_restr *restr)
+{
+       rune_restr_encode(restr, to_sha256, shactx);
+       rune_sha256_endmarker(shactx);
+}
+
+const char *rune_is_derived(const struct rune *source, const struct rune *rune)
+{
+       if (!runestr_eq(source->version, rune->version))
+               return "Version mismatch";
+
+       return rune_is_derived_anyversion(source, rune);
+}
+       
+const char *rune_is_derived_anyversion(const struct rune *source,
+                                      const struct rune *rune)
+{
+       struct sha256_ctx shactx;
+       size_t i;
+
+       if (tal_count(rune->restrs) < tal_count(source->restrs))
+               return "Fewer restrictions than master";
+
+       /* If we add the same restrictions to source rune, do we match? */
+       shactx = source->shactx;
+       for (i = 0; i < tal_count(rune->restrs); i++) {
+               /* First restrictions must be identical */
+               if (i < tal_count(source->restrs)) {
+                       if (!rune_restr_eq(source->restrs[i], rune->restrs[i]))
+                               return "Does not match master restrictions";
+               } else
+                       rune_sha256_add_restr(&shactx, rune->restrs[i]);
+       }
+
+       if (memcmp(shactx.s, rune->shactx.s, sizeof(shactx.s)) != 0)
+               return "Not derived from master";
+       return NULL;
+}
+
+static bool peek_char(const char *data, size_t len, char *c)
+{
+       if (len == 0)
+               return false;
+       *c = *data;
+       return true;
+}
+
+static void drop_char(const char **data, size_t *len)
+{
+       (*data)++;
+       (*len)--;
+}
+
+static void pull_invalid(const char **data, size_t *len)
+{
+       *data = NULL;
+       *len = 0;
+}
+
+static bool pull_char(const char **data, size_t *len, char *c)
+{
+       if (!peek_char(*data, *len, c)) {
+               pull_invalid(data, len);
+               return false;
+       }
+       drop_char(data, len);
+       return true;
+}
+
+bool rune_condition_is_valid(enum rune_condition cond)
+{
+       switch (cond) {
+       case RUNE_COND_IF_MISSING:
+       case RUNE_COND_EQUAL:
+       case RUNE_COND_NOT_EQUAL:
+       case RUNE_COND_BEGINS:
+       case RUNE_COND_ENDS:
+       case RUNE_COND_CONTAINS:
+       case RUNE_COND_INT_LESS:
+       case RUNE_COND_INT_GREATER:
+       case RUNE_COND_LEXO_BEFORE:
+       case RUNE_COND_LEXO_AFTER:
+       case RUNE_COND_COMMENT:
+               return true;
+       }
+       return false;
+}
+
+size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen)
+{
+       for (size_t i = 0; i < alternstrlen; i++) {
+               if (cispunct(alternstr[i]) && alternstr[i] != '_')
+                       return i;
+       }
+       return alternstrlen;
+}
+
+/* Sets *more on success: true if another altern follows */
+static struct rune_altern *rune_altern_decode(const tal_t *ctx,
+                                             const char **data, size_t *len,
+                                             bool *more)
+{
+       struct rune_altern *alt = tal(ctx, struct rune_altern);
+       char *value;
+       size_t strlen;
+       char c;
+
+        /* Swallow field up to possible conditional */
+       strlen = rune_altern_fieldname_len(*data, *len);
+       alt->fieldname = tal_strndup(alt, *data, strlen);
+       *data += strlen;
+       *len -= strlen;
+
+       /* Grab conditional */
+       if (!pull_char(data, len, &c) || !rune_condition_is_valid(c))
+               return tal_free(alt);
+
+       alt->condition = c;
+
+       /* Assign worst case. */
+       value = tal_arr(alt, char, *len + 1);
+       strlen = 0;
+       *more = false;
+       while (*len && pull_char(data, len, &c)) {
+               if (c == '|') {
+                       *more = true;
+                       break;
+               }
+               if (c == '&')
+                       break;
+
+               if (c == '\\' && !pull_char(data, len, &c))
+                       return tal_free(alt);
+               value[strlen++] = c;
+       }
+       value[strlen] = '\0';
+       tal_resize(&value, strlen + 1);
+       alt->value = value;
+       return alt;
+}
+
+static struct rune_restr *rune_restr_decode(const tal_t *ctx,
+                                           const char **data, size_t *len)
+{
+       struct rune_restr *restr = tal(ctx, struct rune_restr);
+       size_t num_alts = 0;
+       bool more;
+
+       /* Must have at least one! */
+       restr->alterns = tal_arr(restr, struct rune_altern *, 0);
+       do {
+               struct rune_altern *alt;
+
+               alt = rune_altern_decode(restr, data, len, &more);
+               if (!alt)
+                       return tal_free(restr);
+               tal_resize(&restr->alterns, num_alts+1);
+               restr->alterns[num_alts++] = alt;
+       } while (more);
+       return restr;
+}
+
+static struct rune *from_string(const tal_t *ctx,
+                               const char *str,
+                               const u8 *hash32)
+{
+       size_t len = strlen(str);
+       struct rune *rune = tal(ctx, struct rune);
+
+       /* Now count up how many bytes we should have hashed: secret uses
+        * first block. */
+       rune->shactx.bytes = 64;
+
+       rune->restrs = tal_arr(rune, struct rune_restr *, 0);
+       rune->unique_id = NULL;
+       rune->version = NULL;
+
+       while (len) {
+               struct rune_restr *restr;
+               restr = rune_restr_decode(rune, &str, &len);
+               if (!restr)
+                       return tal_free(rune);
+               if (!rune_add_restr(rune, restr))
+                       return tal_free(rune);
+       }
+
+       /* Now we replace with canned hash state */
+       memcpy(rune->shactx.s, hash32, 32);
+       for (size_t i = 0; i < 8; i++)
+               rune->shactx.s[i] = be32_to_cpu(rune->shactx.s[i]);
+
+       return rune;
+}
+
+struct rune_restr *rune_restr_from_string(const tal_t *ctx,
+                                         const char *str,
+                                         size_t len)
+{
+       struct rune_restr *restr;
+
+       restr = rune_restr_decode(NULL, &str, &len);
+       /* Don't allow trailing chars */
+       if (restr && len != 0)
+               restr = tal_free(restr);
+       return tal_steal(ctx, restr);
+}
+
+static void to_string(struct wbuf *wbuf, const struct rune *rune, u8 *hash32)
+{
+       /* Copy hash in big-endian */
+       for (size_t i = 0; i < 8; i++) {
+               be32 v = cpu_to_be32(rune->shactx.s[i]);
+               memcpy(hash32 + i*4, &v, sizeof(v));
+       }
+
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               if (i != 0)
+                       to_wbuf("&", 1, wbuf);
+               rune_restr_encode(rune->restrs[i], to_wbuf, wbuf);
+       }
+       to_wbuf("", 1, wbuf);
+}
+
+struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len)
+{
+       size_t blen;
+       u8 *data;
+       struct rune *rune;
+
+       data = tal_arr(NULL, u8, base64_decoded_length(len) + 1);
+
+       blen = base64_decode_using_maps(&base64_maps_urlsafe,
+                                      (char *)data, tal_bytelen(data),
+                                      str, len);
+       if (blen == -1)
+               goto fail;
+
+       if (blen < 32)
+               goto fail;
+
+       data[blen] = '\0';
+       /* Sanity check that it's a valid string! */
+       if (strlen((char *)data + 32) != blen - 32)
+               goto fail;
+
+       rune = from_string(ctx, (const char *)data + 32, data);
+       tal_free(data);
+       return rune;
+
+fail:
+       tal_free(data);
+       return NULL;
+}
+
+struct rune *rune_from_base64(const tal_t *ctx, const char *str)
+{
+       return rune_from_base64n(ctx, str, strlen(str));
+}
+
+char *rune_to_base64(const tal_t *ctx, const struct rune *rune)
+{
+       u8 hash32[32];
+       char *ret;
+       size_t ret_len;
+       struct wbuf wbuf;
+
+       /* We're going to prepend hash */
+       wbuf.off = sizeof(hash32);
+       wbuf.len = 64;
+       wbuf.buf = tal_arr(NULL, char, wbuf.len);
+
+       to_string(&wbuf, rune, hash32);
+       /* Prepend hash */
+       memcpy(wbuf.buf, hash32, sizeof(hash32));
+
+       ret = tal_arr(ctx, char, base64_encoded_length(wbuf.off) + 1);
+       ret_len = base64_encode_using_maps(&base64_maps_urlsafe,
+                                          ret, tal_bytelen(ret),
+                                          wbuf.buf, wbuf.off - 1);
+       ret[ret_len] = '\0';
+       tal_free(wbuf.buf);
+       return ret;
+}
+
+struct rune *rune_from_string(const tal_t *ctx, const char *str)
+{
+       u8 hash[32];
+       if (!hex_decode(str, 64, hash, sizeof(hash)))
+               return NULL;
+       if (str[64] != ':')
+               return NULL;
+       return from_string(ctx, str + 65, hash);
+}
+
+char *rune_to_string(const tal_t *ctx, const struct rune *rune)
+{
+       u8 hash32[32];
+       struct wbuf wbuf;
+
+       /* We're going to prepend hash (in hex), plus colon */
+       wbuf.off = sizeof(hash32) * 2 + 1;
+       wbuf.len = 128;
+       wbuf.buf = tal_arr(ctx, char, wbuf.len);
+
+       to_string(&wbuf, rune, hash32);
+       hex_encode(hash32, sizeof(hash32), wbuf.buf, sizeof(hash32) * 2 + 1);
+       wbuf.buf[sizeof(hash32) * 2] = ':';
+       return wbuf.buf;
+}
diff --git a/ccan/rune/internal.h b/ccan/rune/internal.h
new file mode 100644 (file)
index 0000000..e4de06c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef CCAN_RUNE_INTERNAL_H
+#define CCAN_RUNE_INTERNAL_H
+/* MIT (BSD) license - see LICENSE file for details */
+void rune_sha256_endmarker(struct sha256_ctx *shactx);
+void rune_sha256_add_restr(struct sha256_ctx *shactx,
+                       struct rune_restr *restr);
+bool runestr_eq(const char *a, const char *b);
+#endif /* CCAN_RUNE_INTERNAL_H */
diff --git a/ccan/rune/rune.c b/ccan/rune/rune.c
new file mode 100644 (file)
index 0000000..84296c6
--- /dev/null
@@ -0,0 +1,491 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include "config.h"
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <ccan/endian/endian.h>
+#include <ccan/mem/mem.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/rune/rune.h>
+#include <ccan/rune/internal.h>
+
+/* Helper to produce an id field */
+static struct rune_restr *unique_id_restr(const tal_t *ctx,
+                                         const char *unique_id,
+                                         const char *version)
+{
+       const char *id;
+       struct rune_restr *restr;
+
+       assert(!strchr(unique_id, '-'));
+       if (version)
+               id = tal_fmt(NULL, "%s-%s", unique_id, version);
+       else
+               id = tal_strdup(NULL, unique_id);
+
+       restr = rune_restr_new(ctx);
+        /* We use the empty field for this, since it's always present. */
+       rune_restr_add_altern(restr,
+                             take(rune_altern_new(NULL, "", '=', take(id))));
+       return restr;
+}
+
+/* We pad between fields with something identical to the SHA end marker */
+void rune_sha256_endmarker(struct sha256_ctx *shactx)
+{
+       static const unsigned char pad[64] = {0x80};
+       be64 sizedesc;
+
+       sizedesc = cpu_to_be64((uint64_t)shactx->bytes << 3);
+       /* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
+       sha256_update(shactx, pad, 1 + ((128 - 8 - (shactx->bytes % 64) - 1) % 64));
+       /* Add number of bits of data (big endian) */
+       sha256_update(shactx, &sizedesc, 8);
+}
+
+struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len,
+                     const char *version)
+{
+       struct rune *rune = tal(ctx, struct rune);
+        assert(secret_len + 1 + 8 <= 64);
+
+       if (version)
+               rune->version = tal_strdup(rune, version);
+       else
+               rune->version = NULL;
+       rune->unique_id = NULL;
+       sha256_init(&rune->shactx);
+       sha256_update(&rune->shactx, secret, secret_len);
+       rune_sha256_endmarker(&rune->shactx);
+       rune->restrs = tal_arr(rune, struct rune_restr *, 0);
+       return rune;
+}
+
+struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES)
+{
+       struct rune *dup;
+
+       if (taken(rune))
+               return tal_steal(ctx, (struct rune *)rune);
+
+       dup = tal_dup(ctx, struct rune, rune);
+       dup->restrs = tal_arr(dup, struct rune_restr *, tal_count(rune->restrs));
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               dup->restrs[i] = rune_restr_dup(dup->restrs,
+                                               rune->restrs[i]);
+       }
+       return dup;
+}
+
+struct rune *rune_derive_start(const tal_t *ctx,
+                              const struct rune *master,
+                              const char *unique_id TAKES)
+{
+       struct rune *rune = rune_dup(ctx, master);
+
+        /* If they provide a unique_id, it goes first. */
+       if (unique_id) {
+               if (taken(unique_id))
+                       rune->unique_id = tal_steal(rune, unique_id);
+               else
+                       rune->unique_id = tal_strdup(rune, unique_id);
+               
+               rune_add_restr(rune, take(unique_id_restr(NULL,
+                                                         rune->unique_id,
+                                                         rune->version)));
+       } else {
+               assert(!rune->version);
+       }
+       return rune;
+}
+
+struct rune_altern *rune_altern_new(const tal_t *ctx,
+                                   const char *fieldname TAKES,
+                                   enum rune_condition condition,
+                                   const char *value TAKES)
+{
+       struct rune_altern *altern = tal(ctx, struct rune_altern);
+       altern->condition = condition;
+       altern->fieldname = tal_strdup(altern, fieldname);
+       altern->value = tal_strdup(altern, value);
+       return altern;
+}
+
+struct rune_altern *rune_altern_dup(const tal_t *ctx,
+                                   const struct rune_altern *altern TAKES)
+{
+       struct rune_altern *dup;
+
+       if (taken(altern))
+               return tal_steal(ctx, (struct rune_altern *)altern);
+       dup = tal(ctx, struct rune_altern);
+       dup->condition = altern->condition;
+       dup->fieldname = tal_strdup(dup, altern->fieldname);
+       dup->value = tal_strdup(dup, altern->value);
+       return dup;
+}
+
+struct rune_restr *rune_restr_dup(const tal_t *ctx,
+                                 const struct rune_restr *restr TAKES)
+{
+       struct rune_restr *dup;
+       size_t num_altern;
+
+       if (taken(restr))
+               return tal_steal(ctx, (struct rune_restr *)restr);
+
+       num_altern = tal_count(restr->alterns);
+       dup = tal(ctx, struct rune_restr);
+       dup->alterns = tal_arr(dup, struct rune_altern *, num_altern);
+       for (size_t i = 0; i < num_altern; i++) {
+               dup->alterns[i] = rune_altern_dup(dup->alterns,
+                                                 restr->alterns[i]);
+       }
+       return dup;
+}
+
+struct rune_restr *rune_restr_new(const tal_t *ctx)
+{
+       struct rune_restr *restr = tal(ctx, struct rune_restr);
+       restr->alterns = tal_arr(restr, struct rune_altern *, 0);
+       return restr;
+}
+
+void rune_restr_add_altern(struct rune_restr *restr,
+                          const struct rune_altern *alt TAKES)
+{
+       size_t num = tal_count(restr->alterns);
+
+       tal_resize(&restr->alterns, num+1);
+       restr->alterns[num] = rune_altern_dup(restr->alterns, alt);
+}
+
+static bool is_unique_id(const struct rune_altern *alt)
+{
+       return streq(alt->fieldname, "");
+}
+       
+/* Return unique_id if valid, and sets *version */
+static const char *extract_unique_id(const tal_t *ctx,
+                                    const struct rune_altern *alt,
+                                    const char **version)
+{
+       size_t len;
+       /* Condition must be '='! */
+       if (alt->condition != '=')
+               return NULL;
+
+       len = strcspn(alt->value, "-");
+       if (alt->value[len])
+               *version = tal_strdup(ctx, alt->value + len + 1);
+       else
+               *version = NULL;
+       return tal_strndup(ctx, alt->value, len);
+}
+
+bool rune_add_restr(struct rune *rune,
+                   const struct rune_restr *restr TAKES)
+{
+       size_t num = tal_count(rune->restrs);
+
+       /* An empty fieldname is additional correctness checks */
+       for (size_t i = 0; i < tal_count(restr->alterns); i++) {
+               if (!is_unique_id(restr->alterns[i]))
+                       continue;
+
+               /* Must be the only alternative */
+               if (tal_count(restr->alterns) != 1)
+                       goto fail;
+               /* Must be the first restriction */
+               if (num != 0)
+                       goto fail;
+
+               rune->unique_id = extract_unique_id(rune,
+                                                   restr->alterns[i],
+                                                   &rune->version);
+               if (!rune->unique_id)
+                       goto fail;
+       }
+
+       tal_resize(&rune->restrs, num+1);
+       rune->restrs[num] = rune_restr_dup(rune->restrs, restr);
+
+       rune_sha256_add_restr(&rune->shactx, rune->restrs[num]);
+       return true;
+
+fail:
+       if (taken(restr))
+               tal_free(restr);
+       return false;
+}
+
+static const char *rune_restr_test(const tal_t *ctx,
+                                  const struct rune *rune,
+                                  const struct rune_restr *restr,
+                                  const char *(*check)(const tal_t *ctx,
+                                                       const struct rune *rune,
+                                                       const struct rune_altern *alt,
+                                                       void *arg),
+                                  void *arg)
+{
+       size_t num = tal_count(restr->alterns);
+       const char **errs = tal_arr(NULL, const char *, num);
+       char *err;
+
+       /* Only one alternative has to pass! */
+       for (size_t i = 0; i < num; i++) {
+               errs[i] = check(errs, rune, restr->alterns[i], arg);
+               if (!errs[i]) {
+                       tal_free(errs);
+                       return NULL;
+               }
+       }
+
+       err = tal_fmt(ctx, "%s", errs[0]);
+       for (size_t i = 1; i < num; i++)
+               tal_append_fmt(&err, " AND %s", errs[i]);
+       tal_free(errs);
+       return err;
+}
+
+static const char *cond_test(const tal_t *ctx,
+                            const struct rune_altern *alt,
+                            const char *complaint,
+                            bool cond)
+{
+       if (cond)
+               return NULL;
+
+       return tal_fmt(ctx, "%s %s %s", alt->fieldname, complaint, alt->value);
+}
+
+static const char *integer_compare_valid(const tal_t *ctx,
+                                        const s64 *fieldval_int,
+                                        const struct rune_altern *alt,
+                                        s64 *runeval_int)
+{
+       long l;
+       char *p;
+
+       if (!fieldval_int)
+               return tal_fmt(ctx, "%s is not an integer field",
+                              alt->fieldname);
+
+       errno = 0;
+       l = strtol(alt->value, &p, 10);
+       if (p == alt->value
+           || *p
+           || ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE))
+               return tal_fmt(ctx, "%s is not a valid integer", alt->value);
+
+       *runeval_int = l;
+       return NULL;
+}
+
+static int lexo_order(const char *fieldval_str,
+                     size_t fieldval_strlen,
+                     const char *alt)
+{
+       int ret = strncmp(fieldval_str, alt, fieldval_strlen);
+
+       /* If alt is same but longer, fieldval is < */
+       if (ret == 0 && strlen(alt) > fieldval_strlen)
+               ret = -1;
+       return ret;
+}
+
+static const char *rune_alt_single(const tal_t *ctx,
+                                  const struct rune_altern *alt,
+                                  const char *fieldval_str,
+                                  size_t fieldval_strlen,
+                                  const s64 *fieldval_int)
+{
+       char strfield[STR_MAX_CHARS(s64) + 1];
+       s64 runeval_int = 0 /* gcc v9.4.0 gets upset with uninitiaized var at -O3 */;
+       const char *err;
+
+       /* Caller can't set both! */
+       if (fieldval_int) {
+               assert(!fieldval_str);
+               sprintf(strfield, "%"PRIi64, *fieldval_int);
+               fieldval_str = strfield;
+               fieldval_strlen = strlen(strfield);
+       }
+
+       switch (alt->condition) {
+       case RUNE_COND_IF_MISSING:
+               if (!fieldval_str)
+                       return NULL;
+               return tal_fmt(ctx, "%s is present", alt->fieldname);
+       case RUNE_COND_EQUAL:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is not equal to",
+                                memeqstr(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_NOT_EQUAL:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to",
+                                !memeqstr(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_BEGINS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not start with",
+                                memstarts_str(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_ENDS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not end with",
+                                memends_str(fieldval_str, fieldval_strlen, alt->value));
+       case RUNE_COND_CONTAINS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not contain",
+                                memmem(fieldval_str, fieldval_strlen,
+                                       alt->value, strlen(alt->value)));
+       case RUNE_COND_INT_LESS:
+               err = integer_compare_valid(ctx, fieldval_int,
+                                           alt, &runeval_int);
+               if (err)
+                       return err;
+               return cond_test(ctx, alt, "is greater or equal to",
+                                *fieldval_int < runeval_int);
+       case RUNE_COND_INT_GREATER:
+               err = integer_compare_valid(ctx, fieldval_int,
+                                           alt, &runeval_int);
+               if (err)
+                       return err;
+               return cond_test(ctx, alt, "is less or equal to",
+                                *fieldval_int > runeval_int);
+       case RUNE_COND_LEXO_BEFORE:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to or ordered after",
+                                lexo_order(fieldval_str, fieldval_strlen, alt->value) < 0);
+       case RUNE_COND_LEXO_AFTER:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to or ordered before",
+                                lexo_order(fieldval_str, fieldval_strlen, alt->value) > 0);
+       case RUNE_COND_COMMENT:
+               return NULL;
+       }
+       /* We should never create any other values! */
+       abort();
+}
+
+const char *rune_alt_single_str(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               const char *fieldval_str,
+                               size_t fieldval_strlen)
+{
+       return rune_alt_single(ctx, alt, fieldval_str, fieldval_strlen, NULL);
+}
+
+const char *rune_alt_single_int(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               s64 fieldval_int)
+{
+       return rune_alt_single(ctx, alt, NULL, 0, &fieldval_int);
+}
+
+const char *rune_alt_single_missing(const tal_t *ctx,
+                                   const struct rune_altern *alt)
+{
+       return rune_alt_single(ctx, alt, NULL, 0, NULL);
+}
+
+const char *rune_meets_criteria_(const tal_t *ctx,
+                                const struct rune *rune,
+                                const char *(*check)(const tal_t *ctx,
+                                                     const struct rune *rune,
+                                                     const struct rune_altern *alt,
+                                                     void *arg),
+                                void *arg)
+{
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               const char *err;
+
+               /* Don't "check" unique id */
+               if (i == 0 && is_unique_id(rune->restrs[i]->alterns[0]))
+                       continue;
+               
+               err = rune_restr_test(ctx, rune, rune->restrs[i], check, arg);
+               if (err)
+                       return err;
+       }
+       return NULL;
+}
+
+const char *rune_test_(const tal_t *ctx,
+                      const struct rune *master,
+                      const struct rune *rune,
+                      const char *(*check)(const tal_t *ctx,
+                                           const struct rune *rune,
+                                           const struct rune_altern *alt,
+                                           void *arg),
+                      void *arg)
+{
+       const char *err;
+
+       err = rune_is_derived(master, rune);
+       if (err)
+               return err;
+       return rune_meets_criteria_(ctx, rune, check, arg);
+}
+
+bool rune_altern_eq(const struct rune_altern *alt1,
+                   const struct rune_altern *alt2)
+{
+       return alt1->condition == alt2->condition
+               && streq(alt1->fieldname, alt2->fieldname)
+               && streq(alt1->value, alt2->value);
+}
+
+bool rune_restr_eq(const struct rune_restr *rest1,
+                  const struct rune_restr *rest2)
+{
+       if (tal_count(rest1->alterns) != tal_count(rest2->alterns))
+               return false;
+
+       for (size_t i = 0; i < tal_count(rest1->alterns); i++)
+               if (!rune_altern_eq(rest1->alterns[i], rest2->alterns[i]))
+                       return false;
+       return true;
+}
+
+/* Equal, as in both NULL, or both non-NULL and matching */
+bool runestr_eq(const char *a, const char *b)
+{
+       if (a) {
+               if (!b)
+                       return false;
+               return streq(a, b);
+       } else
+               return b == NULL;
+}
+
+bool rune_eq(const struct rune *rune1, const struct rune *rune2)
+{
+       if (!runestr_eq(rune1->unique_id, rune2->unique_id))
+               return false;
+       if (!runestr_eq(rune1->version, rune2->version))
+               return false;
+
+       if (memcmp(rune1->shactx.s, rune2->shactx.s, sizeof(rune1->shactx.s)))
+               return false;
+       if (rune1->shactx.bytes != rune2->shactx.bytes)
+               return false;
+       if (memcmp(rune1->shactx.buf.u8, rune2->shactx.buf.u8,
+                  rune1->shactx.bytes % 64))
+               return false;
+
+       if (tal_count(rune1->restrs) != tal_count(rune2->restrs))
+               return false;
+
+       for (size_t i = 0; i < tal_count(rune1->restrs); i++)
+               if (!rune_restr_eq(rune1->restrs[i], rune2->restrs[i]))
+                       return false;
+       return true;
+}
diff --git a/ccan/rune/rune.h b/ccan/rune/rune.h
new file mode 100644 (file)
index 0000000..c373269
--- /dev/null
@@ -0,0 +1,400 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_RUNE_RUNE_H
+#define CCAN_RUNE_RUNE_H
+#include <ccan/crypto/sha256/sha256.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
+#include <ccan/tal/tal.h>
+#include <ccan/short_types/short_types.h>
+
+/* A rune is a series of restrictions. */
+struct rune {
+       /* unique_id (if any) */
+       const char *unique_id;
+       /* Version (if any) */
+       const char *version;
+
+       /* SHA-2 256 of restrictions so far. */
+       struct sha256_ctx shactx;
+       /* Length given by tal_count() */
+       struct rune_restr **restrs;
+};
+
+/* A restriction is one or more alternatives (altern) */
+struct rune_restr {
+       /* Length given by tal_count() */
+       struct rune_altern **alterns;
+};
+
+enum rune_condition {
+       RUNE_COND_IF_MISSING = '!',
+       RUNE_COND_EQUAL = '=',
+       RUNE_COND_NOT_EQUAL = '/',
+       RUNE_COND_BEGINS = '^',
+       RUNE_COND_ENDS = '$',
+       RUNE_COND_CONTAINS = '~',
+       RUNE_COND_INT_LESS = '<',
+       RUNE_COND_INT_GREATER = '>',
+       RUNE_COND_LEXO_BEFORE = '{',
+       RUNE_COND_LEXO_AFTER = '}',
+       RUNE_COND_COMMENT = '#',
+};
+
+/* An alternative is a utf-8 fieldname, a condition, and a value */
+struct rune_altern {
+       enum rune_condition condition;
+       /* Strings. */
+       const char *fieldname, *value;
+};
+
+/**
+ * rune_new - Create an unrestricted rune from this secret.
+ * @ctx: tal context, or NULL.  Freeing @ctx will free the returned rune.
+ * @secret: secret bytes.
+ * @secret_len: number of @secret bytes (must be 55 bytes or less)
+ * @version: if non-NULL, sets a version for this rune.
+ *
+ * This allocates a new, unrestricted rune (sometimes called a master rune).
+ *
+ * Setting a version allows for different interpretations of a rune if
+ * things change in future, at cost of some space when it's used.
+ *
+ * Example:
+ *  u8 secret[16];
+ *  struct rune *master;
+ *
+ *  // A secret determined with a fair die roll!
+ *  memset(secret, 5, sizeof(secret));
+ *  master = rune_new(NULL, secret, sizeof(secret), NULL);
+ *  assert(master);
+ */
+struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len,
+                     const char *version);
+
+/**
+ * rune_derive_start - Copy master rune, add a unique id.
+ * @ctx: context to allocate rune off
+ * @master: master rune.
+ * @unique_id: unique id; can be NULL, but that's not recommended.
+ *
+ * It's usually recommended to assign each rune a unique_id, so that
+ * specific runes can be blacklisted later (otherwise you need to disable
+ * all runes).  This enlarges the rune string by '=<unique_id>' however.
+ *
+ * The rune version will be the same as the master: if that's non-zero,
+ * you *must* set unique_id.
+ *
+ * @unique_id cannot contain '-'.
+ *
+ * Example:
+ *  struct rune *rune;
+ *  // In reality, some global incrementing variable.
+ *  const char *id = "1";
+ *  rune = rune_derive_start(NULL, master, id);
+ *  assert(rune);
+ */
+struct rune *rune_derive_start(const tal_t *ctx,
+                              const struct rune *master,
+                              const char *unique_id);
+
+/**
+ * rune_dup - Copy a rune.
+ * @ctx: tal context, or NULL.
+ * @altern: the altern to copy.
+ *
+ * If @altern is take(), then simply returns it, otherwise copies.
+ */
+struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES);
+
+/**
+ * rune_altern_new - Create a new alternative.
+ * @ctx: tal context, or NULL.  Freeing @ctx will free the returned altern.
+ * @fieldname: the UTF-8 field for the altern.  You can only have
+ *             alphanumerics, '.', '-' and '_' here.
+ * @condition: the condition, defined above.
+ * @value: the value for comparison; use "" if you don't care.  Any UTF-8 value
+ *         is allowed.
+ *
+ * An altern is the basis of rune restrictions (technically, a restriction
+ * is one or more alterns, but it's often just one).
+ *
+ * Example:
+ *  struct rune_altern *a1, *a2;
+ *  a1 = rune_altern_new(NULL, "val", RUNE_COND_EQUAL, "7");
+ *  a2 = rune_altern_new(NULL, "val2", '>', "-1");
+ *  assert(a1 && a2);
+ */
+struct rune_altern *rune_altern_new(const tal_t *ctx,
+                                   const char *fieldname TAKES,
+                                   enum rune_condition condition,
+                                   const char *value TAKES);
+
+/**
+ * rune_altern_dup - copy an alternative.
+ * @ctx: tal context, or NULL.
+ * @altern: the altern to copy.
+ *
+ * If @altern is take(), then simply returns it, otherwise copies.
+ */
+struct rune_altern *rune_altern_dup(const tal_t *ctx,
+                                   const struct rune_altern *altern TAKES);
+
+/**
+ * rune_restr_new - Create a new (empty) restriction.
+ * @ctx: tal context, or NULL.  Freeing @ctx will free the returned restriction.
+ *
+ * Example:
+ *  struct rune_restr *restr = rune_restr_new(NULL);
+ *  assert(restr);
+ */
+struct rune_restr *rune_restr_new(const tal_t *ctx);
+
+/**
+ * rune_restr_dup - copy a restr.
+ * @ctx: tal context, or NULL.
+ * @restr: the restr to copy.
+ *
+ * If @resttr is take(), then simply returns it, otherwise copies.
+ */
+struct rune_restr *rune_restr_dup(const tal_t *ctx,
+                                 const struct rune_restr *restr TAKES);
+
+/**
+ * rune_restr_add_altern - add an altern to this restriction
+ * @restr: the restriction to add to
+ * @alt: the altern.
+ *
+ * If the alt is take(alt) then the alt will be owned by the restriction,
+ * otherwise it's copied.
+ *
+ * Example:
+ *  rune_restr_add_altern(restr, take(a1));
+ *  rune_restr_add_altern(restr, take(a2));
+ */
+void rune_restr_add_altern(struct rune_restr *restr,
+                          const struct rune_altern *alt TAKES);
+
+/**
+ * rune_add_restr - add a restriction to this rune
+ * @rune: the rune to add to.
+ * @restr: the (non-empty) restriction.
+ *
+ * If the alt is take(alt) then the alt will be owned by the restr,
+ * otherwise it's copied (and all its children are copied!).
+ *
+ * This fails (and returns false) if restr tries to set unique_id/version
+ * and is not the first restriction, or has more than one alternative,
+ * or uses a non '=' condition.
+ *
+ * Example:
+ *  rune_add_restr(rune, take(restr));
+ */
+bool rune_add_restr(struct rune *rune,
+                   const struct rune_restr *restr TAKES);
+
+/**
+ * rune_altern_eq - are two rune_altern equivalent?
+ * @alt1: the first
+ * @alt2: the second
+ */
+bool rune_altern_eq(const struct rune_altern *alt1,
+                   const struct rune_altern *alt2);
+
+/**
+ * rune_restr_eq - are two rune_restr equivalent?
+ * @rest1: the first
+ * @rest2: the second
+ */
+bool rune_restr_eq(const struct rune_restr *rest1,
+                  const struct rune_restr *rest2);
+
+/**
+ * rune_eq - are two runes equivalent?
+ * @rest1: the first
+ * @rest2: the second
+ */
+bool rune_eq(const struct rune *rune1, const struct rune *rune2);
+
+/**
+ * rune_alt_single_str - helper to implement check().
+ * @ctx: context to allocate any error return from.
+ * @alt: alternative to test.
+ * @fieldval_str: field value as a string.
+ * @fieldval_strlen: length of @fieldval_str
+ */
+const char *rune_alt_single_str(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               const char *fieldval_str,
+                               size_t fieldval_strlen);
+
+/**
+ * rune_alt_single_int - helper to implement check().
+ * @ctx: context to allocate any error return from.
+ * @alt: alternative to test.
+ * @fieldval_int: field value as an integer.
+ */
+const char *rune_alt_single_int(const tal_t *ctx,
+                               const struct rune_altern *alt,
+                               s64 fieldval_int);
+
+/**
+ * rune_alt_single_missing - helper to implement check().
+ * @ctx: context to allocate any error return from.
+ * @alt: alternative to test.
+ *
+ * Use this if alt->fieldname is unknown (it could still pass, if
+ * the test is that the fieldname is missing).
+ */
+const char *rune_alt_single_missing(const tal_t *ctx,
+                                   const struct rune_altern *alt);
+
+
+/**
+ * rune_is_derived - is a rune derived from this other rune?
+ * @source: the base rune (usually the master rune)
+ * @rune: the rune to check.
+ *
+ * This is the first part of "is this rune valid?": does the cryptography
+ * check out, such that they validly made the rune from this source rune?
+ *
+ * It also checks that the versions match: if you want to allow more than
+ * one version, see rune_is_derived_anyversion.
+ */
+const char *rune_is_derived(const struct rune *source, const struct rune *rune);
+
+/**
+ * rune_is_derived_anyversion - is a rune derived from this other rune?
+ * @source: the base rune (usually the master rune)
+ * @rune: the rune to check.
+ *
+ * This does not check source->version against rune->version: if you issue
+ * different rune versions you will need to check that yourself.
+ */
+const char *rune_is_derived_anyversion(const struct rune *source,
+                                      const struct rune *rune);
+
+/**
+ * rune_meets_criteria - do we meet the criteria specified by the rune?
+ * @ctx: the tal context to allocate the returned error off.
+ * @rune: the rune to check.
+ * @check: the callback to check values
+ * @arg: data to hand to @check
+ *
+ * This is the second part of "is this rune valid?".
+ */
+const char *rune_meets_criteria_(const tal_t *ctx,
+                                const struct rune *rune,
+                                const char *(*check)(const tal_t *ctx,
+                                                     const struct rune *rune,
+                                                     const struct rune_altern *alt,
+                                                     void *arg),
+                                void *arg);
+
+/* Typesafe wrapper */
+#define rune_meets_criteria(ctx, rune, check, arg)                     \
+       rune_meets_criteria_(typesafe_cb_preargs(const char *, void *,  \
+                                                (ctx), (rune),         \
+                                                (check), (arg),        \
+                                                const tal_t *,         \
+                                                const struct rune *,   \
+                                                const struct rune_altern *), \
+                            (arg))
+
+/**
+ * rune_test - is a rune authorized?
+ * @ctx: the tal context to allocate @errstr off.
+ * @master: the master rune created from secret.
+ * @rune: the rune to check.
+ * @errstr: if non-NULL, descriptive string of failure.
+ * @get: the callback to get values
+ * @arg: data to hand to callback
+ *
+ * Simple call for rune_is_derived() and rune_meets_criteria().  If
+ * it's not OK, returns non-NULL.
+ */
+const char *rune_test_(const tal_t *ctx,
+                      const struct rune *master,
+                      const struct rune *rune,
+                      const char *(*check)(const tal_t *ctx,
+                                           const struct rune *rune,
+                                           const struct rune_altern *alt,
+                                           void *arg),
+                      void *arg);
+
+/* Typesafe wrapper */
+#define rune_test(ctx_, master_, rune_, check_, arg_)                  \
+       rune_test_((ctx_), (master_), (rune_),                          \
+                  typesafe_cb_preargs(const char *, void *,            \
+                                      (check_), (arg_),                \
+                                      const tal_t *,                   \
+                                      const struct rune *,             \
+                                      const struct rune_altern *),     \
+                  (arg_))
+
+
+/**
+ * rune_from_base64 - convert base64 string to rune.
+ * @ctx: context to allocate rune off.
+ * @str: base64 string.
+ *
+ * Returns NULL if it's malformed.
+ */
+struct rune *rune_from_base64(const tal_t *ctx, const char *str);
+
+/**
+ * rune_from_base64n - convert base64 string to rune.
+ * @ctx: context to allocate rune off.
+ * @str: base64 string.
+ * @len: length of @str.
+ *
+ * Returns NULL if it's malformed.
+ */
+struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len);
+
+/**
+ * rune_to_base64 - convert run to base64 string.
+ * @ctx: context to allocate rune off.
+ * @rune: the rune.
+ *
+ * Only returns NULL if you've allowed tal allocations to return NULL.
+ */
+char *rune_to_base64(const tal_t *ctx, const struct rune *rune);
+
+/**
+ * This is a much more convenient working form.
+ */
+struct rune *rune_from_string(const tal_t *ctx, const char *str);
+char *rune_to_string(const tal_t *ctx, const struct rune *rune);
+
+/**
+ * rune_restr_from_string - convenience routine to parse a single restriction.
+ * @ctx: context to allocate rune off.
+ * @str: the string of form "<field><cond><val>[|<field><cond><val>]*"
+ * @len: the length of @str.
+ *
+ * This is useful for writing simple tests and making simple runes.
+ */
+struct rune_restr *rune_restr_from_string(const tal_t *ctx,
+                                         const char *str,
+                                         size_t len);
+
+/**
+ * rune_condition_is_valid: is this a valid condition?
+ * @cond: potential condition character.
+ *
+ * Returns true if it's one of enum rune_condition.
+ */
+bool rune_condition_is_valid(enum rune_condition cond);
+
+/**
+ * rune_altern_fieldname_len: how much of this string is condition?
+ * @alternstr: potential alternative string
+ * @alternstrlen: length
+ *
+ * This helps parsing your own runes.
+ *
+ * Returns the first possible condition (check with rune_condition_is_valid)
+ * or alternstrlen if none found.
+ */
+size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen);
+
+#endif /* CCAN_RUNE_RUNE_H */
diff --git a/ccan/rune/test/run-alt-lexicographic-order.c b/ccan/rune/test/run-alt-lexicographic-order.c
new file mode 100644 (file)
index 0000000..a37ee58
--- /dev/null
@@ -0,0 +1,33 @@
+#include <ccan/rune/rune.c>
+#include <ccan/rune/coding.c>
+#include <ccan/tal/str/str.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       const char *str = "test string";
+       plan_tests(strlen(str) * strlen(str));
+
+       for (size_t i = 0; str[i]; i++) {
+               char *stra = strdup(str);
+               stra[i] = '\0';
+               for (size_t j = 0; str[j]; j++) {
+                       char *strb = strdup(str);
+                       strb[j] = '\0';
+                       int lexo, strc;
+
+                       lexo = lexo_order(str, i, strb);
+                       strc = strcmp(stra, strb);
+                       if (strc > 0)
+                               ok1(lexo > 0);
+                       else if (strc < 0)
+                               ok1(lexo < 0);
+                       else
+                               ok1(lexo == 0);
+                       free(strb);
+               }
+               free(stra);
+       }
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/rune/test/run-altern-escape.c b/ccan/rune/test/run-altern-escape.c
new file mode 100644 (file)
index 0000000..550cafa
--- /dev/null
@@ -0,0 +1,37 @@
+#include <ccan/rune/rune.c>
+#include <ccan/rune/coding.c>
+#include <ccan/tal/grab_file/grab_file.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       static const u8 secret_zero[16];
+       struct rune *rune;
+       struct rune_restr *restr;
+       const tal_t *ctx = tal(NULL, char);
+
+       plan_tests(9);
+       restr = rune_restr_from_string(ctx, "desc=@tipjar\\|jb55@sendsats.lol",
+                                      strlen("desc=@tipjar\\|jb55@sendsats.lol"));
+       ok1(tal_count(restr->alterns) == 1);
+       ok1(restr->alterns[0]->condition == '=');
+       ok1(streq(restr->alterns[0]->fieldname, "desc"));
+       ok1(streq(restr->alterns[0]->value, "@tipjar|jb55@sendsats.lol"));
+
+       rune = rune_new(ctx, secret_zero, sizeof(secret_zero), NULL); 
+       rune_add_restr(rune, take(restr));
+
+       /* Converting via base64 should not change it! */
+       rune = rune_from_base64(ctx, rune_to_base64(ctx, rune));
+       ok1(tal_count(rune->restrs) == 1);
+       restr = rune->restrs[0];
+       ok1(tal_count(restr->alterns) == 1);
+       ok1(restr->alterns[0]->condition == '=');
+       ok1(streq(restr->alterns[0]->fieldname, "desc"));
+       ok1(streq(restr->alterns[0]->value, "@tipjar|jb55@sendsats.lol"));
+       
+       tal_free(ctx);
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/rune/test/run.c b/ccan/rune/test/run.c
new file mode 100644 (file)
index 0000000..d90b701
--- /dev/null
@@ -0,0 +1,127 @@
+#include <ccan/rune/rune.c>
+#include <ccan/rune/coding.c>
+#include <ccan/tal/grab_file/grab_file.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/tap/tap.h>
+
+static const char *check(const tal_t *ctx,
+                        const struct rune *rune,
+                        const struct rune_altern *alt,
+                        char **parts)
+{
+       const char *val = NULL;
+
+       for (size_t i = 1; parts[i]; i++) {
+               if (strstarts(parts[i], alt->fieldname)
+                   && parts[i][strlen(alt->fieldname)] == '=')
+                       val = parts[i] + strlen(alt->fieldname) + 1;
+       }
+
+       /* If it's an integer, hand it like that */
+       if (val) {
+               char *endp;
+               s64 v = strtol(val, &endp, 10);
+               if (*endp == '\0' && endp != val)
+                       return rune_alt_single_int(ctx, alt, v);
+               return rune_alt_single_str(ctx, alt, val, strlen(val));
+       }
+       return rune_alt_single_missing(ctx, alt);
+}
+
+int main(void)
+{
+       char *vecs;
+       char **lines;
+       static const u8 secret_zero[16];
+       struct rune *mr;
+
+       /* Test vector rune uses all-zero secret */
+       mr = rune_new(NULL, secret_zero, sizeof(secret_zero), NULL); 
+
+       /* Python runes library generates test vectors */
+       vecs = grab_file(mr, "test/test_vectors.csv");
+       assert(vecs);
+       lines = tal_strsplit(mr, take(vecs), "\n", STR_NO_EMPTY);
+
+       plan_tests(355);
+
+       for (size_t i = 0; lines[i]; i++) {
+               struct rune *rune1, *rune2;
+               char **parts;
+
+               parts = tal_strsplit(lines, lines[i], ",", STR_EMPTY_OK);
+               if (streq(parts[0], "VALID")) {
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_string(parts, parts[2]);
+                       ok1(rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(rune2);
+                       ok1(rune_eq(rune1, rune2));
+                       ok1(streq(rune_to_string(parts, rune2), parts[2]));
+                       ok1(streq(rune_to_base64(parts, rune1), parts[3]));
+                       ok1(rune_is_derived_anyversion(mr, rune1) == NULL);
+                       ok1(rune_is_derived_anyversion(mr, rune2) == NULL);
+
+                       if (parts[4]) {
+                               if (parts[5])
+                                       ok1(streq(rune1->version, parts[5]));
+                               ok1(streq(rune1->unique_id, parts[4]));
+                       } else {
+                               ok1(!rune1->version);
+                               ok1(!rune1->unique_id);
+                       }
+                       mr->version = NULL;
+               } else if (streq(parts[0], "DERIVE")) {
+                       struct rune_restr *restr;
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_base64(parts, parts[2]);
+                       ok1(rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(rune2);
+                       ok1(rune_is_derived_anyversion(mr, rune1) == NULL);
+                       ok1(rune_is_derived_anyversion(mr, rune2) == NULL);
+                       ok1(rune_is_derived_anyversion(rune1, rune2) == NULL);
+
+                       restr = rune_restr_new(NULL);
+                       for (size_t j = 4; parts[j]; j+=3) {
+                               struct rune_altern *alt;
+                               alt = rune_altern_new(NULL,
+                                                     parts[j],
+                                                     parts[j+1][0],
+                                                     parts[j+2]);
+                               rune_restr_add_altern(restr, take(alt));
+                       }
+                       rune_add_restr(rune1, take(restr));
+                       ok1(rune_eq(rune1, rune2));
+               } else if (streq(parts[0], "MALFORMED")) {
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_string(parts, parts[2]);
+                       ok1(!rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(!rune2);
+               } else if (streq(parts[0], "BAD DERIVATION")) {
+                       diag("test %s %s", parts[0], parts[1]);
+                       rune1 = rune_from_string(parts, parts[2]);
+                       ok1(rune1);
+                       rune2 = rune_from_base64(parts, parts[3]);
+                       ok1(rune2);
+                       ok1(rune_eq(rune1, rune2));
+                       ok1(rune_is_derived(mr, rune1) != NULL);
+                       ok1(rune_is_derived(mr, rune2) != NULL);
+               } else {
+                       const char *err;
+                       diag("test %s", parts[0]);
+                       err = rune_test(parts, mr, rune1, check, parts);
+                       if (streq(parts[0], "PASS")) {
+                               ok1(!err);
+                       } else {
+                               assert(streq(parts[0], "FAIL"));
+                               ok1(err);
+                       }
+               }
+       }
+
+       tal_free(mr);
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/rune/test/test_vectors.csv b/ccan/rune/test/test_vectors.csv
new file mode 100644 (file)
index 0000000..880ea3c
--- /dev/null
@@ -0,0 +1,156 @@
+VALID,empty rune (secret = [0]*16),374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb:,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=
+PASS
+PASS,f1=1
+PASS,f1=var
+PASS,f1=\|\&\\
+VALID,unique id 1,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:=1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,1
+VALID,unique id 2 version 1,4520773407c9658646326fdffe685ffbc3c8639a080dae4310b371830a205cf1:=2-1,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,2,1
+VALID,f1 is missing,64a926b7185d7cf98e10a07dfc4e83d2a826896ebdb112ac964566fa2d50b464:f1!,ZKkmtxhdfPmOEKB9_E6D0qgmiW69sRKslkVm-i1QtGRmMSE=
+PASS
+PASS,f2=f1
+FAIL,f1=1
+FAIL,f1=var
+VALID,f1 equals v1,745c6e39cd41ee9f8388af8ad882bae4ee4e8f6b373f7682cc64d8574551fa5f:f1=v1,dFxuOc1B7p-DiK-K2IK65O5Oj2s3P3aCzGTYV0VR-l9mMT12MQ==
+PASS,f1=v1
+FAIL,f1=v
+FAIL,f1=v1a
+FAIL
+FAIL,f2=f1
+VALID,f1 not equal v1,c9236a6532bfa8e24bec9a66e96af3fb355f817770e79c5a81f6dd0b5ed20e47:f1/v1,ySNqZTK_qOJL7Jpm6Wrz-zVfgXdw55xagfbdC17SDkdmMS92MQ==
+PASS,f1=v2
+PASS,f1=v
+PASS,f1=v1a
+FAIL
+FAIL,f2=v1
+VALID,f1 ends with v1,71f2a1ec9631efc75b01db15fe1f025327ab467f8a83e6bfa7506da222adc5a2:f1$v1,cfKh7JYx78dbAdsV_h8CUyerRn-Kg-a_p1BtoiKtxaJmMSR2MQ==
+PASS,f1=v1
+PASS,f1=2v1
+FAIL,f1=v1a
+FAIL
+VALID,f1 starts with v1,5b13dffbbd9f7b191b0557595d10b22c0acec0c567f8efeba1d7d047927d7bce:f1^v1,WxPf-72fexkbBVdZXRCyLArOwMVn-O_rodfQR5J9e85mMV52MQ==
+PASS,f1=v1
+PASS,f1=v1a
+FAIL,f1=2v1
+FAIL
+VALID,f1 contains v1,ccbe593b72e0ab29446e46796ccd0c775ecd7a327fcc9ddc00fd3910cdacca00:f1~v1,zL5ZO3LgqylEbkZ5bM0Md17NejJ_zJ3cAP05EM2sygBmMX52MQ==
+PASS,f1=v1
+PASS,f1=v1a
+PASS,f1=2v1
+PASS,f1=2v12
+FAIL,f1=1v2
+FAIL
+VALID,f1 less than v1,caff52cedb9241dc00aea7cefc2b89b0a7445b1a4e34c48a5a2b91d2fe76d31f:f1<v1,yv9SztuSQdwArqfO_CuJsKdEWxpONMSKWiuR0v520x9mMTx2MQ==
+FAIL,f1=1
+FAIL,f1=2
+FAIL,f1=v1
+FAIL
+VALID,f1 less than 1,f9776db54fb54c8dd6af20a65a0f210a752a0ee4d1b0a0e7fd9d7ef65af76f84:f1<1,-XdttU-1TI3WryCmWg8hCnUqDuTRsKDn_Z1-9lr3b4RmMTwx
+PASS,f1=0
+PASS,f1=-10000
+FAIL,f1=1
+FAIL,f1=10000
+FAIL,f1=v1
+FAIL
+VALID,f1 greater than v1,2135748f1956d9dfa3c5b09ab6af9d6bb06a41c5bcf93d3f8105cb278af5ac56:f1>v1,ITV0jxlW2d-jxbCatq-da7BqQcW8-T0_gQXLJ4r1rFZmMT52MQ==
+FAIL,f1=1
+FAIL,f1=2
+FAIL,f1=v1
+FAIL
+VALID,f1 greater than 1,84e9991dd941bac97cc681eefec5dd7ac3668a4490ca6b0f19f0e79d2bb9c746:f1>1,hOmZHdlBusl8xoHu_sXdesNmikSQymsPGfDnnSu5x0ZmMT4x
+PASS,f1=2
+PASS,f1=10000
+FAIL,f1=1
+FAIL,f1=-10000
+FAIL,f1=0
+FAIL,f1=v1
+FAIL
+VALID,f1 sorts before 11,b9653ad0dcad7e5ed183f98cdd7e616acd07a98cc66a107a67626290bf000236:f1{11,uWU60Nytfl7Rg_mM3X5has0HqYzGahB6Z2JikL8AAjZmMXsxMQ==
+PASS,f1=0
+PASS,f1=1
+PASS,f1=       
+PASS,f1=/
+FAIL,f1=11
+FAIL,f1=111
+FAIL,f1=v1
+FAIL,f1=:
+FAIL
+VALID,f1 sorts after 11,8c1f6c7c39badc5dea850192a0a4c6e9dd96bf33d410adc5a08fc375b22a1a52:f1}11,jB9sfDm63F3qhQGSoKTG6d2WvzPUEK3FoI_DdbIqGlJmMX0xMQ==
+PASS,f1=111
+PASS,f1=v1
+PASS,f1=:
+FAIL,f1=0
+FAIL,f1=1
+FAIL,f1=       
+FAIL,f1=/
+FAIL,f1=11
+FAIL
+VALID,f1 comment 11,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMQ==
+PASS,f1=111
+PASS,f1=v1
+PASS,f1=:
+PASS,f1=0
+PASS,f1=1
+PASS,f1=       
+PASS,f1=/
+PASS,f1=11
+PASS
+VALID,f_with_underscores equals v1,ee979e1f2c376d69923aab0e8e001111963af038bdce394ffd7ecdc9e7020a6e:f_with_underscores=v1,7peeHyw3bWmSOqsOjgAREZY68Di9zjlP_X7NyecCCm5mX3dpdGhfdW5kZXJzY29yZXM9djE=
+PASS,f_with_underscores=v1
+FAIL,f_with_underscores=v
+FAIL,f_with_underscores=v1a
+FAIL
+FAIL,f2=f_with_underscores
+VALID,f1=1 or f2=3,85c3643dc102f0a0d6f20eeb8c294092151688fae41ef7c8ec7272ab23918376:f1=1|f2=3,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=
+PASS,f1=1
+PASS,f1=1,f2=2
+PASS,f2=3
+PASS,f1=var,f2=3
+PASS,f1=1,f2=3
+FAIL
+FAIL,f1=2
+FAIL,f1=f1
+FAIL,f2=1
+FAIL,f2=f1
+DERIVE,unique_id 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,,=,1
+DERIVE,unique_id 2 version 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,,=,2-1
+DERIVE,f1=1 or f2=3,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,f1,=,1,f2,=,3
+DERIVE,AND f3 contains &|\,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,S253BW1Lragb1CpCSLXYGt9AdrE4iFMlXmnO0alV5vlmMT0xfGYyPTMmZjN-XCZcfFxc,f3,~,&|\
+PASS,f1=1,f3=&|\
+PASS,f2=3,f3=&|\x
+FAIL
+FAIL,f1=1
+FAIL,f2=3
+FAIL,f1=1,f2=3
+FAIL,f1=2,f3=&|\
+FAIL,f2=2,f3=&|\
+FAIL,f3=&|\
+MALFORMED,unique id must use = not !,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:!1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0hMQ==
+MALFORMED,unique id must use = not /,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:/1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0vMQ==
+MALFORMED,unique id must use = not ^,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:^1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1eMQ==
+MALFORMED,unique id must use = not $,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:$1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0kMQ==
+MALFORMED,unique id must use = not ~,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:~1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1-MQ==
+MALFORMED,unique id must use = not <,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:<1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL08MQ==
+MALFORMED,unique id must use = not >,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:>1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0-MQ==
+MALFORMED,unique id must use = not },6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:}1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL19MQ==
+MALFORMED,unique id must use = not {,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:{1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL17MQ==
+MALFORMED,unique id cannot be overridden,7a63a2966d38e6fed89256d4a6e983a6813bf084d4fc6c20b9cdaef24b23fa7e:=1-2&=3,emOilm045v7YklbUpumDpoE78ITU_Gwguc2u8ksj-n49MS0yJj0z
+MALFORMED,version cannot be overridden,db823224f960976b3ee142ce8899fc7ea461b42617e7d16167b1886c5988c628:=1-2&=1-3,24IyJPlgl2s-4ULOiJn8fqRhtCYX59FhZ7GIbFmIxig9MS0yJj0xLTM=
+MALFORMED,Bad condition ",76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1"11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSIxMQ==
+MALFORMED,Bad condition &,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1&11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSYxMQ==
+MALFORMED,Bad condition ',76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1'11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMScxMQ==
+MALFORMED,Bad condition (,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1(11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSgxMQ==
+MALFORMED,Bad condition ),76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1)11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSkxMQ==
+MALFORMED,Bad condition *,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1*11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSoxMQ==
+MALFORMED,Bad condition +,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1+11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSsxMQ==
+MALFORMED,Bad condition -,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1-11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS0xMQ==
+MALFORMED,Bad condition .,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1.11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS4xMQ==
+MALFORMED,Bad condition :,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1:11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMToxMQ==
+MALFORMED,Bad condition ;,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1;11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMTsxMQ==
+MALFORMED,Bad condition ?,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1?11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMT8xMQ==
+MALFORMED,Bad condition [,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1[11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVsxMQ==
+MALFORMED,Bad condition \,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1\11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVwxMQ==
+MALFORMED,Bad condition ],76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1]11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV0xMQ==
+MALFORMED,Bad condition `,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1`11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMWAxMQ==
+MALFORMED,Bad condition |,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1|11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMXwxMQ==
+BAD DERIVATION,Incremented sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0e:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw5mMSMxMQ==
+BAD DERIVATION,Unchanged sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11&a=1,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMSZhPTE=
index d34de811b3542dbea10610f875f554dc4e734085..8eb4ab42fc812d32f5b0bb99c2b31e82fc0b90b2 100644 (file)
@@ -22,7 +22,8 @@ static int stringbuilder_cpy(
        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);
        }
index 9fa51d0d40db4cf89fb16041585d71ea163b2238..16a30e036ad44c3492a9e05074747b7b15a6bff6 100644 (file)
@@ -17,9 +17,8 @@ struct node {
 };
 
 /* 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. */
@@ -35,20 +34,26 @@ static struct strmap *closest(struct strmap *n, const char *member)
        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);
@@ -68,7 +73,7 @@ bool strmap_add_(struct strmap *map, const char *member, const void *value)
        }
 
        /* 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++) {
index 0b32e6befc0ca2974ad7304dd967e1c95fae75d3..8724c31dcbb20d2686cd33cbc39149209af7b767 100644 (file)
@@ -72,7 +72,7 @@ static inline bool strmap_empty_(const struct strmap *map)
 /**
  * strmap_get - get a value from a string map
  * @map: the typed strmap to search.
- * @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).
  *
@@ -85,6 +85,23 @@ static inline bool strmap_empty_(const struct strmap *map)
        tcon_cast((map), canary, strmap_get_(tcon_unwrap(map), (member)))
 void *strmap_get_(const struct strmap *map, const char *member);
 
+/**
+ * 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.
index 05d52e1dbe5cab4409461c42b2c26ed57b009987..1230d8cacafcd2bfb05b659435480ba0ac77f9c5 100644 (file)
@@ -28,7 +28,8 @@ enum prop_type {
 
 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;
@@ -36,7 +37,8 @@ struct tal_hdr {
 
 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 {
@@ -72,7 +74,7 @@ static struct {
        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,
@@ -123,9 +125,11 @@ void tal_cleanup(void)
 }
 
 /* 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
@@ -174,8 +178,11 @@ static struct tal_hdr *to_tal_hdr(const void *ctx)
        check_bounds(ignore_destroying_bit(t->parent_child));
        check_bounds(t->list.next);
        check_bounds(t->list.prev);
-       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;
 }
 
@@ -215,13 +222,12 @@ static void notify(const struct tal_hdr *ctx,
                   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;
@@ -255,29 +261,54 @@ static void *allocate(size_t size)
        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;
 }
 
@@ -287,7 +318,7 @@ static void init_property(struct prop_hdr *hdr,
 {
        hdr->type = type;
        hdr->next = parent->prop;
-       parent->prop = hdr;
+       parent->prop = (char *)hdr;
 }
 
 static struct notifier *add_notifier_property(struct tal_hdr *t,
@@ -321,17 +352,20 @@ static enum tal_notify_type del_notifier_property(struct tal_hdr *t,
                                                  bool match_extra_arg,
                                                  void *extra_arg)
 {
-        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;
 
@@ -341,8 +375,8 @@ static enum tal_notify_type del_notifier_property(struct tal_hdr *t,
                    && 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;
@@ -388,7 +422,8 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child)
 
 static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
 {
-       struct prop_hdr **prop, *p, *next;
+       struct prop_hdr *prop;
+       char *ptr, *next;
 
        assert(!taken(from_tal_hdr(t)));
 
@@ -402,10 +437,10 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
        notify(t, TAL_NOTIFY_FREE, (tal_t *)orig, saved_errno);
 
        /* Now free children and groups. */
-       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);
@@ -414,9 +449,9 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
        }
 
         /* 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);
 }
@@ -590,25 +625,34 @@ bool tal_del_destructor2_(const tal_t *ctx, void (*destroy)(void *me, void *arg)
 bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
 {
         struct tal_hdr *t = debug_tal(to_tal_hdr(ctx));
-        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;
 
@@ -620,15 +664,16 @@ bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
 
 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)
@@ -767,11 +812,17 @@ out:
 }
 
 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);
@@ -797,11 +848,16 @@ void *tal_dup_(const tal_t *ctx, const void *p, size_t size,
        }
 
        ret = tal_alloc_arr_(ctx, size, n + extra, false, label);
-       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 *),
@@ -821,36 +877,38 @@ void tal_set_backend(void *(*alloc_fn)(size_t size),
 static void dump_node(unsigned int indent, const struct tal_hdr *t)
 {
        unsigned int i;
-        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**", pp->type);
+                       fprintf(stderr, " **UNKNOWN(%p):%i**", prop, prop->type);
                }
        }
        fprintf(stderr, "\n");
@@ -862,7 +920,7 @@ static void tal_dump_(unsigned int level, const struct tal_hdr *t)
 
        dump_node(level, t);
 
-       children = find_property(t, CHILDREN);
+       children = find_property((struct tal_hdr *)t, CHILDREN);
        if (children) {
                struct tal_hdr *i;
 
@@ -893,7 +951,8 @@ static bool check_err(struct tal_hdr *t, const char *errorstr,
 static bool check_node(struct children *parent_child,
                       struct tal_hdr *t, const char *errorstr)
 {
-       struct prop_hdr *p;
+       struct prop_hdr *prop;
+       char *p;
        struct name *name = NULL;
        struct children *children = NULL;
 
@@ -903,23 +962,24 @@ static bool check_node(struct children *parent_child,
        if (ignore_destroying_bit(t->parent_child) != parent_child)
                return check_err(t, errorstr, "incorrect parent");
 
-       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;
@@ -927,7 +987,7 @@ static bool check_node(struct children *parent_child,
                        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");
index a3a1549153fe4f42636b116d33162c774982df46..c486f9e8e194a99b51ffdb8117788bbae86a89ed 100644 (file)
@@ -352,10 +352,21 @@ tal_t *tal_parent(const tal_t *ctx);
  * tal_dup - duplicate an object.
  * @ctx: The tal allocated object to be parent of the result (may be NULL).
  * @type: the type (should match type of @p!)
- * @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.
@@ -369,7 +380,17 @@ tal_t *tal_parent(const tal_t *ctx);
        tal_dup_arr_label(ctx, type, p, n, extra, TAL_LABEL(type, "[]"))
 
 
-
+/**
+ * tal_dup_arr - 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))
@@ -379,13 +400,13 @@ tal_t *tal_parent(const tal_t *ctx);
        ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), false, label))
 #define tal_arrz_label(ctx, type, count, label)                                        \
        ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), true, label))
-#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))
 
 /**
@@ -505,7 +526,9 @@ void *tal_alloc_arr_(const tal_t *ctx, size_t bytes, size_t count, bool clear,
                     const char *label);
 
 void *tal_dup_(const tal_t *ctx, const void *p TAKES, size_t size,
-              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);
 
index 150f00adae9e2d60c0df655ff2ac105e34c2c85a..47e436408cbe4ce803951eabb59410e2d6fde1d2 100644 (file)
@@ -13,8 +13,8 @@ static void *my_realloc(void *old, size_t size)
        void *new = realloc(old, size);
        if (new == old) {
                void *p = malloc(size);
-               memcpy(p, old, size);
-               free(old);
+               memcpy(p, new, size);
+               free(new);
                new = p;
        }
        return new;
index 29f055588015f5e38f02513c6dcce39b4657ff20..5cc3352a4a90df99362ac74ea0e9a6635ccafec4 100644 (file)
@@ -56,7 +56,7 @@ static unsigned bucket_of(ssize_t min, unsigned step_bits, ssize_t val)
                return 0;
        }
        assert(step_bits < SIZET_BITS);
-       return (size_t)(val - min) >> step_bits;
+       return ((size_t)val - (size_t)min) >> step_bits;
 }
 
 /* Return the min value in bucket b. */
@@ -67,7 +67,7 @@ static ssize_t bucket_min(ssize_t min, unsigned step_bits, unsigned b)
                return min;
        }
        assert(step_bits < SIZET_BITS);
-       return min + ((ssize_t)b << step_bits);
+       return min + ((size_t)b << step_bits);
 }
 
 /* Does shifting by this many bits truncate the number? */
@@ -76,6 +76,9 @@ static bool shift_overflows(size_t num, unsigned bits)
        if (bits == 0) {
                return false;
        }
+       if (bits >= SIZET_BITS) {
+               return true;
+       }
 
        return ((num << bits) >> 1) != (num << (bits - 1));
 }
@@ -94,7 +97,7 @@ static void renormalize(struct tally *tally,
 
        /* If we don't have sufficient range, increase step bits until
         * buckets cover entire range of ssize_t anyway. */
-       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. */
@@ -113,11 +116,13 @@ static void renormalize(struct tally *tally,
        memset(tally->counts, 0, sizeof(tally->counts[0]) * old_min);
 
        /* If we moved boundaries, adjust buckets to that ratio. */
-       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:
index e0f84b383976c4d106e6d9fe3ee9043c449d1ed8..df3aac88b785a4acc241e125a9c377766a55272d 100644 (file)
  * 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 *)
index 44645a7ec6d417875ceed8910915efea3a0d2193..ed1d3e206acd97eee8cf46b69e8018a5a87096e4 100644 (file)
@@ -25,7 +25,7 @@ struct info_tcon {
 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
index 19ba5bdcc915d22851009a630b4bda32dd85f242..a03f6514e1793b0dce08c45cc23cd34c7078d01c 100644 (file)
@@ -21,7 +21,7 @@ int main(void)
 {
        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
index 9185225a9361903e25e86ce616e31da0e502e577..dfdfdba9a3412ef692311762b9cdfdd54d01c4e5 100644 (file)
@@ -25,7 +25,7 @@ struct info_tcon {
 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
index 958e5c8b3dca47c67c1f23aa25fe9fc29e759e90..a56e510f1e2bb86f326b917ad61bc17c839281b2 100644 (file)
@@ -21,7 +21,7 @@ int main(void)
 {
        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
diff --git a/ccan/ungraph/LICENSE b/ccan/ungraph/LICENSE
new file mode 120000 (symlink)
index 0000000..2354d12
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/ungraph/_info b/ccan/ungraph/_info
new file mode 100644 (file)
index 0000000..6088c5e
--- /dev/null
@@ -0,0 +1,77 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * ungraph - extract a graph from an ASCII diagram.
+ *
+ * This code takes an ASCII diagram and converts it to a graph.
+ * The following things are assumed:
+ * 1. The input consists of \n-terminated lines
+ * 2. /-\|+ are used for edges.
+ * 3. <^>v are used for arrowheads.
+ * 4. + can be used to cross-over.
+ * 5. No arrowheads or both-ended arrowheads are shortcuts for "both ways".
+ * 6. Edges can turn with or without a +, by up to 90 degrees.
+ * 7. Edges must go from one node name to another.
+ * 8. Any other text is an edge label which must be next to an edge or
+ *    another label.
+ *
+ * License: BSD-MIT
+ * Example:
+ * // Convert an ASCII graph to Graphviz dot format
+ * #include <ccan/err/err.h>
+ * #include <ccan/grab_file/grab_file.h>
+ * #include <ccan/ungraph/ungraph.h>
+ *
+ * // Just return the name as our node.
+ * static void *add_node(const tal_t *ctx,
+ *                       const char *name,
+ *                       const char **errstr,
+ *                       void *unused)
+ * {
+ *         return (void *)name;
+ * }
+ *
+ * static const char *add_edge(const tal_t *ctx,
+ *                             void *source_node,
+ *                             void *dest_node,
+ *                             bool bidir,
+ *                             const char **labels,
+ *                             void *arg)
+ * {
+ *         printf("%s -> %s;\n", 
+ *                (char *)source_node, (char *)dest_node);
+ *         if (bidir)
+ *                 printf("%s -> %s;\n", 
+ *                        (char *)dest_node, (char *)source_node);
+ *         return NULL;
+ * }
+ *
+ * int main(int argc, char *argv[])
+ * {
+ *         const char *graph = grab_file(NULL, argv[1], NULL), *errmsg;
+ *        printf("digraph %s {\n", argv[1] ? argv[1] : "stdin");
+ *         errmsg = ungraph(NULL, graph, add_node, add_edge, NULL);
+ *         if (errmsg)
+ *                 errx(1, "%s", errmsg);
+ *         printf("}");
+ * }
+ *
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/tal\n");
+               printf("ccan/tal/str\n");
+               printf("ccan/typesafe_cb\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/ungraph/test/run.c b/ccan/ungraph/test/run.c
new file mode 100644 (file)
index 0000000..a9ae9be
--- /dev/null
@@ -0,0 +1,140 @@
+#include <ccan/ungraph/ungraph.h>
+/* Include the C files directly. */
+#include <ccan/ungraph/ungraph.c>
+#include <ccan/tap/tap.h>
+#include <assert.h>
+
+static void *add_node(const tal_t *ctx,
+                     const char *name,
+                     const char **errstr,
+                     char **out)
+{
+       tal_append_fmt(out, "add_node %s\n", (char *)name);
+       return (void *)tal_steal(ctx, name);
+}
+
+static const char *add_edge(const tal_t *ctx,
+                           void *source_node,
+                           void *dest_node,
+                           bool bidir,
+                           const char **labels,
+                           char **out)
+{
+       tal_append_fmt(out, "add_edge %s-%s bidir=%i\n",
+                      (char *)source_node,
+                      (char *)dest_node,
+                      bidir);
+       for (size_t i = 0; i < tal_count(labels); i++)
+               tal_append_fmt(out, "- label %s\n", labels[i]);
+       return NULL;
+}
+
+int main(void)
+{
+       const tal_t *ctx = tal(NULL, char);
+       char *out = tal_arrz(ctx, char, 1);
+       /* This is how many tests you plan to run */
+       plan_tests(16);
+
+       ok1(ungraph(ctx,
+                   "AAA----->BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=0\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA<------BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge BBB-AAA bidir=0\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA------BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=1\n") == 0);
+
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA<------>BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=1\n") == 0);
+
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA\n"
+                   " ^ \n"
+                   " | \n"
+                   " | \n"
+                   " v \n"
+                   "BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=1\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA\n"
+                   " / \n"
+                   "| \n"
+                   " \\ \n"
+                   "  v \n"
+                   "  BBB\n",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=0\n") == 0);
+       
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   "AAA\n"
+                   " / \n"
+                   "|xyx \n"
+                   " \\ \n"
+                   "  v \n"
+                   "  BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_edge AAA-BBB bidir=0\n"
+                  "- label xyx\n") == 0);
+
+       out = tal_arrz(ctx, char, 1);
+       ok1(ungraph(ctx,
+                   " AAA \n"
+                   "  |   \n"
+                   "A-+----B \n"
+                   "  | LABEL  \n"
+                   "  |  xyz\n"
+                   "  v  \n"
+                   " BBB",
+                   add_node, add_edge, &out) == NULL);
+       ok1(strcmp(out,
+                  "add_node AAA\n"
+                  "add_node BBB\n"
+                  "add_node A\n"
+                  "add_node B\n"
+                  "add_edge AAA-BBB bidir=0\n"
+                  "add_edge A-B bidir=1\n"
+                  "- label LABEL\n"
+                  "- label xyz\n") == 0);
+
+       tal_free(ctx);
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/ungraph/ungraph.c b/ccan/ungraph/ungraph.c
new file mode 100644 (file)
index 0000000..ba29d22
--- /dev/null
@@ -0,0 +1,721 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include <ccan/ungraph/ungraph.h>
+#include <ccan/tal/str/str.h>
+
+struct xy {
+       size_t x, y;
+};
+
+struct text {
+       struct xy xy;
+       size_t width;
+       const char *text;
+       /* If it's a node, this is non-NULL */
+       void *node;
+       /* NULL if none found, edge it one found, self if > 1 */
+       struct edge *nearest_edge;
+};
+
+struct edge {
+       struct text *src, *dst;
+       bool bidir;
+       const char **labels;
+};
+
+/* This means we actually found two "nearest_edge" */
+static struct edge fake_edge;
+
+#define EDGES "+/-\\|"
+#define ARROWS "<^>v"
+
+enum dir {
+       UP,
+       UP_RIGHT,
+       RIGHT,
+       DOWN_RIGHT,
+       DOWN,
+       DOWN_LEFT,
+       LEFT,
+       UP_LEFT,
+       INVALID,
+};
+
+static enum dir opposite_dir(enum dir dir)
+{
+       return (dir + 4) % 8;
+}
+
+static enum dir clockwise(enum dir dir)
+{
+       return (dir + 1) % 8;
+}
+
+static enum dir anticlockwise(enum dir dir)
+{
+       return (dir + 7) % 8;
+}
+
+static enum dir dir_away(const struct text *t, struct xy xy)
+{
+       int xdir, ydir;
+       enum dir dirs[3][3] = {{UP_LEFT, UP, UP_RIGHT},
+                              {LEFT, INVALID, RIGHT},
+                              {DOWN_LEFT, DOWN, DOWN_RIGHT}};
+       
+       if (xy.y < t->xy.y)
+               ydir = -1;
+       else if (xy.y > t->xy.y)
+               ydir = 1;
+       else
+               ydir = 0;
+       if (xy.x >= t->xy.x + t->width)
+               xdir = 1;
+       else if (xy.x < t->xy.x)
+               xdir = -1;
+       else
+               xdir = 0;
+
+       return dirs[ydir+1][xdir+1];
+}
+
+static char line_for_dir(enum dir dir)
+{
+       switch (dir) {
+       case UP:
+       case DOWN:
+               return '|';
+       case UP_RIGHT:
+       case DOWN_LEFT:
+               return '/';
+       case RIGHT:
+       case LEFT:
+               return '-';
+       case DOWN_RIGHT:
+       case UP_LEFT:
+               return '\\';
+       case INVALID:
+               break;
+       }
+       abort();
+}
+
+static char arrow_for_dir(enum dir dir)
+{
+       switch (dir) {
+       case UP:
+       case UP_RIGHT:
+       case UP_LEFT:
+               return '^';
+       case DOWN:
+       case DOWN_RIGHT:
+       case DOWN_LEFT:
+               return 'v';
+       case LEFT:
+               return '<';
+       case RIGHT:
+               return '>';
+       case INVALID:
+               break;
+       }
+       abort();
+}
+
+static struct xy move_in_dir(struct xy xy, enum dir dir)
+{
+       switch (dir) {
+       case UP:
+               xy.y = xy.y - 1;
+               return xy;
+       case DOWN:
+               xy.y = xy.y + 1;
+               return xy;
+       case UP_RIGHT:
+               xy.x = xy.x + 1;
+               xy.y = xy.y - 1;
+               return xy;
+       case DOWN_LEFT:
+               xy.x = xy.x - 1;
+               xy.y = xy.y + 1;
+               return xy;
+       case RIGHT:
+               xy.x = xy.x + 1;
+               return xy;
+       case LEFT:
+               xy.x = xy.x - 1;
+               return xy;
+       case DOWN_RIGHT:
+               xy.x = xy.x + 1;
+               xy.y = xy.y + 1;
+               return xy;
+       case UP_LEFT:
+               xy.x = xy.x - 1;
+               xy.y = xy.y - 1;
+               return xy;
+       case INVALID:
+               break;
+       }
+       abort();
+}
+
+static char *sqc(char **sq, struct xy xy)
+{
+       return &sq[xy.y][xy.x];
+}
+
+/* Try straight ahead first, then a bit to either side, then
+ * finally left and right */
+static struct xy scan_move_next(struct xy xy, enum dir start, enum dir *cur)
+{
+       if (*cur == start)
+               *cur = clockwise(start);
+       else if (*cur == clockwise(start))
+               *cur = anticlockwise(start);
+       else if (*cur == anticlockwise(start))
+               *cur = anticlockwise(anticlockwise(start));
+       else if (*cur == anticlockwise(anticlockwise(start)))
+               *cur = clockwise(clockwise(start));
+       else {
+               *cur = INVALID;
+               return xy;
+       }
+       return move_in_dir(xy, *cur);
+}
+
+static void start_perimeter(struct xy *xyp, enum dir *dirp, struct xy xy)
+{
+       *dirp = RIGHT;
+       xyp->x = xy.x - 1;
+       xyp->y = xy.y - 1;
+}
+
+static void next_perimeter(struct xy *xyp, enum dir *dirp, struct xy xy, size_t width)
+{
+       *xyp = move_in_dir(*xyp, *dirp);
+       if (*dirp == RIGHT && xyp->x == xy.x + width)
+               *dirp = DOWN;
+       else if (*dirp == DOWN && xyp->y == xy.y + 1)
+               *dirp = LEFT;
+       else if (*dirp == LEFT && xyp->x == xy.x - 1)
+               *dirp = UP;
+       else if (*dirp == UP && xyp->y == xy.y - 2)
+               *dirp = INVALID;
+}
+
+/* Useful iterators. */
+#define for_each_scan_dir(xyp, dirp, xy, dir)                  \
+       for (*dirp = dir, *xyp = move_in_dir(xy, *dirp);        \
+            *dirp != INVALID;                                  \
+            *xyp = scan_move_next(xy, dir, dirp))
+
+#define for_each_perimeter(xyp, dirp, xy, width)       \
+       for (start_perimeter(xyp, dirp, xy);            \
+            *dirp != INVALID;                          \
+            next_perimeter(xyp, dirp, xy, width))
+
+/* Canonicalizes str into array of characters, finds text strings. */
+static char **square(const tal_t *ctx,
+                    const char *str,
+                    struct text **texts)
+{
+       size_t width = 0, height = 0;
+       size_t line_len = 0;
+       char **sq;
+       struct text *cur_text;
+       size_t strlen;
+
+       *texts = tal_arr(ctx, struct text, 0);
+
+       strlen = 0;
+       for (size_t i = 0; str[i]; i++) {
+               if (str[i] == '\n') {
+                       height++;
+                       line_len = 0;
+               } else {
+                       line_len++;
+                       if (line_len > width)
+                               width = line_len;
+               }
+               strlen++;
+       }
+
+       /* If didn't end in \n, it's implied */
+       if (line_len != 0) {
+               height++;
+               strlen++;
+       }
+
+       /* For analysis simplicity, create a blank border. */
+       sq = tal_arr(ctx, char *, height + 2);
+       for (size_t i = 0; i < height + 2; i++) {
+               sq[i] = tal_arr(sq, char, width + 2);
+               memset(sq[i], ' ', width + 2);
+       }
+
+       /* Copy across and find text */
+       cur_text = NULL;
+       width = height = 1;
+       for (size_t i = 0; i < strlen; i++) {
+               bool end_text;
+               bool eol;
+
+               eol = (str[i] == '\n' || str[i] == '\0');
+               if (!eol)
+                       sq[height][width] = str[i];
+
+               /* v by itself handled separately below */
+               if (strchr(EDGES ARROWS "\n", str[i]) && str[i] != 'v') {
+                       end_text = (cur_text != NULL);
+               } else if (cur_text) {
+                       /* Two spaces ends text */
+                       end_text = (str[i] == ' ' && str[i-1] == ' ') || eol;
+               } else if (str[i] != ' ') {
+                       size_t num_texts = tal_count(*texts);
+                       tal_resize(texts, num_texts+1);
+                       cur_text = &(*texts)[num_texts];
+                       cur_text->xy.x = width;
+                       cur_text->xy.y = height;
+                       cur_text->width = 0;
+                       cur_text->node = NULL;
+                       cur_text->nearest_edge = NULL;
+                       end_text = false;
+               } else
+                       end_text = false;
+
+               if (end_text) {
+                       /* Trim final space */
+                       if (sq[cur_text->xy.y][cur_text->xy.x + cur_text->width-1] == ' ')
+                               cur_text->width--;
+                       /* Ignore lone 'v' */
+                       if (cur_text->width == 1 && sq[cur_text->xy.y][cur_text->xy.x] == 'v')
+                               tal_resize(texts, tal_count(*texts)-1);
+                       else {
+                               cur_text->text = tal_strndup(ctx, &sq[cur_text->xy.y][cur_text->xy.x],
+                                                            cur_text->width);
+                       }
+                       cur_text = NULL;
+               }
+
+               if (cur_text)
+                       cur_text->width++;
+               if (eol) {
+                       height++;
+                       width = 1;
+               } else
+                       width++;
+       }
+
+       return sq;
+}
+
+/* If text was not previously a node, it is now! */
+static const char *text_now_node(const tal_t *ctx,
+                                char **sq,
+                                struct text *text,
+                                void *(*add_node)(const tal_t *ctx,
+                                                  const char *name,
+                                                  const char **errstr,
+                                                  void *arg),
+                                void *arg)
+{
+       const char *err;
+
+       /* Already a node? */
+       if (text->node)
+               return NULL;
+
+       text->node = add_node(ctx, text->text, &err, arg);
+       if (!text->node)
+               return err;
+       return NULL;
+}
+
+static bool correct_line_char(char c, enum dir dir, enum dir *newdir)
+{
+       if (c == line_for_dir(dir)) {
+               *newdir = dir;
+               return true;
+       } else if (c == line_for_dir(anticlockwise(dir))) {
+               *newdir = anticlockwise(dir);
+               return true;
+       } else if (c == line_for_dir(clockwise(dir))) {
+               *newdir = clockwise(dir);
+               return true;
+       }
+       return false;
+}
+
+static bool seek_line(char **sq, struct xy *xy, enum dir *dir)
+{
+       struct xy scan;
+       enum dir scandir;
+
+       for_each_scan_dir(&scan, &scandir, *xy, *dir) {
+               if (correct_line_char(*sqc(sq, scan), scandir, &scandir))
+                       goto found;
+               /* + in front always works */
+               if (*dir == scandir && *sqc(sq, scan) == '+')
+                       goto found;
+       }
+       return false;
+
+found:
+       *xy = scan;
+       *dir = scandir;
+       return true;
+}
+
+static bool seek_arrowhead(char **sq, struct xy *xy, enum dir *dir)
+{
+       struct xy scan;
+       enum dir scandir;
+
+       for_each_scan_dir(&scan, &scandir, *xy, *dir) {
+               if (strchr(ARROWS, *sqc(sq, scan))) {
+                       *xy = scan;
+                       *dir = scandir;
+                       return true;
+               }
+       }
+       return false;
+}
+
+static struct text *in_text(struct text *texts, struct xy xy)
+{
+       for (size_t i = 0; i < tal_count(texts); i++) {
+               if (texts[i].xy.y != xy.y)
+                       continue;
+               if (xy.x >= texts[i].xy.x + texts[i].width)
+                       continue;
+               if (xy.x < texts[i].xy.x)
+                       continue;
+               return texts + i;
+       }
+       return NULL;
+}
+
+static struct text *seek_text(struct text *texts,
+                             struct xy xy, enum dir dir)
+{
+       struct xy scan;
+       enum dir scandir;
+
+       for_each_scan_dir(&scan, &scandir, xy, dir) {
+               struct text *t = in_text(texts, scan);
+               if (t)
+                       return t;
+       }
+       return NULL;
+}
+
+static void erase_line(char **sq,
+                      struct xy xy,
+                      enum dir dir,
+                      enum dir prev_dir)
+{
+       char c = ' ';
+
+       /* If we go straight through a +, convert for crossover */
+       if (prev_dir == dir && *sqc(sq, xy) == '+') {
+               if (dir == UP || dir == DOWN)
+                       c = '-';
+               if (dir == LEFT || dir == RIGHT)
+                       c = '|';
+       }
+       *sqc(sq, xy) = c;
+}
+
+static bool in_nearby(struct text **nearby, const struct text *t)
+{
+       for (size_t i = 0; i < tal_count(nearby); i++) {
+               if (nearby[i] == t)
+                       return true;
+       }
+       return false;
+}
+
+static void add_nearby(struct text ***nearby,
+                      struct text *texts,
+                      struct xy xy)
+{
+       struct xy perim;
+       enum dir pdir;
+       size_t n = tal_count(*nearby);
+
+       for_each_perimeter(&perim, &pdir, xy, 1) {
+               struct text *t = in_text(texts, perim);
+               if (!t)
+                       continue;
+               /* Don't care if it's already a node */
+               if (t->node)
+                       continue;
+               if (in_nearby(*nearby, t))
+                       continue;
+               tal_resize(nearby, n+1);
+               (*nearby)[n++] = t;
+       }
+}
+
+/* Clears lines as it goes. */
+static struct text *follow_line(char **sq,
+                               struct text *texts,
+                               struct xy xy,
+                               enum dir dir,
+                               bool *arrow_src,
+                               bool *arrow_dst,
+                               bool *dangling,
+                               struct text ***nearby)
+{
+       char expect_arrow = arrow_for_dir(opposite_dir(dir));
+       enum dir prev_dir;
+
+       *nearby = tal_arr(sq, struct text *, 0);
+
+       if (*sqc(sq, xy) == expect_arrow) {
+               *arrow_src = true;
+       } else if (*sqc(sq, xy) == line_for_dir(dir)) {
+               *arrow_src = false;
+       } else if (*sqc(sq, xy) == line_for_dir(anticlockwise(dir))) {
+               *arrow_src = false;
+               dir = anticlockwise(dir);
+       } else if (*sqc(sq, xy) == line_for_dir(clockwise(dir))) {
+               *arrow_src = false;
+               dir = clockwise(dir);
+       } else {
+               *dangling = false;
+               /* No arrow is fine. */
+               return NULL;
+       }
+
+       erase_line(sq, xy, dir, INVALID);
+       add_nearby(nearby, texts, xy);
+
+       *arrow_dst = false;
+       prev_dir = dir;
+       for (;;) {
+               /* Try to continue line */
+               if (!*arrow_dst && seek_line(sq, &xy, &dir)) {
+                       erase_line(sq, xy, dir, prev_dir);
+                       add_nearby(nearby, texts, xy);
+                       prev_dir = dir;
+                       continue;
+               }
+               /* Look for arrow */
+               if (!*arrow_dst && seek_arrowhead(sq, &xy, &dir)) {
+                       erase_line(sq, xy, dir, prev_dir);
+                       add_nearby(nearby, texts, xy);
+                       *arrow_dst = true;
+                       prev_dir = dir;
+                       continue;
+               }
+               break;
+       }
+
+       /* Must be in text! */
+       *dangling = true;
+       return seek_text(texts, xy, dir);
+}
+
+static const char *try_create_edge(const tal_t *ctx,
+                                  char **sq,
+                                  struct text *texts,
+                                  struct xy xy,
+                                  struct text *src,
+                                  void *(*add_node)(const tal_t *ctx,
+                                                    const char *name,
+                                                    const char **errstr,
+                                                    void *arg),
+                                  void *arg,
+                                  struct edge **edge)
+{
+       struct text *dst;
+       bool arrow_src, arrow_dst, dangling;
+       struct text **nearby;
+       const char *err;
+
+       *edge = NULL;
+       if (in_text(texts, xy))
+               return NULL;
+
+       dst = follow_line(sq, texts, xy, dir_away(src, xy), &arrow_src, &arrow_dst, &dangling, &nearby);
+       if (!dst) {
+               if (dangling)
+                       return tal_fmt(ctx, "Found dangling arrow at (%zu,%zu)", xy.x-1, xy.y-1);
+               return NULL;
+       }
+
+       /* If you weren't a node before, you are now! */
+       err = text_now_node(ctx, sq, src, add_node, arg);
+       if (err)
+               return err;
+       err = text_now_node(ctx, sq, dst, add_node, arg);
+       if (err)
+               return err;
+
+       /* No arrows equiv to both arrows */
+       if (!arrow_src && !arrow_dst)
+               arrow_src = arrow_dst = true;
+
+       *edge = tal(NULL, struct edge);
+       if (arrow_dst) {
+               (*edge)->src = src;
+               (*edge)->dst = dst;
+               (*edge)->bidir = arrow_src;
+       } else {
+               (*edge)->src = dst;
+               (*edge)->dst = src;
+               (*edge)->bidir = false;
+       }
+       (*edge)->labels = tal_arr(*edge, const char *, 0);
+
+       /* Now record any texts it passed by, in case they're labels */
+       for (size_t i = 0; i < tal_count(nearby); i++) {
+               /* We might have just made it a node */
+               if (nearby[i]->node)
+                       continue;
+               /* Already has an edge?  Mark it as near two, to error
+                * later if it's a label */
+               if (nearby[i]->nearest_edge)
+                       nearby[i]->nearest_edge = &fake_edge;
+               else
+                       nearby[i]->nearest_edge = *edge;
+       }
+               
+       return NULL;
+}
+
+static const char *scan_for_unused(const tal_t *ctx,
+                                  struct text *texts,
+                                  char **sq)
+{
+       struct xy xy;
+       for (xy.y = 0; xy.y < tal_count(sq); xy.y++) {
+               for (xy.x = 0; xy.x < tal_count(sq[xy.y]); xy.x++) {
+                       if (in_text(texts, xy))
+                               continue;
+                       if (*sqc(sq,xy) != ' ')
+                               return tal_fmt(ctx, "Unused '%c' at (%zu,%zu)",
+                                              *sqc(sq, xy), xy.x-1, xy.y-1);
+               }
+       }
+       return NULL;
+}
+
+static void add_label(struct edge *edge, const struct text *label)
+{
+       size_t n = tal_count(edge->labels);
+       tal_resize(&edge->labels, n+1);
+       edge->labels[n] = label->text;
+}
+       
+const char *ungraph_(const tal_t *ctx,
+                    const char *str,
+                    void *(*add_node)(const tal_t *ctx,
+                                      const char *name,
+                                      const char **errstr,
+                                      void *arg),
+                    const char *(*add_edge)(const tal_t *ctx,
+                                            void *source_node,
+                                            void *dest_node,
+                                            bool bidir,
+                                            const char **labels,
+                                            void *arg),
+                    void *arg)
+{
+       /* To hold all our temporaries! */
+       const tal_t *sub = tal(ctx, char);
+       char **sq;
+       struct text *texts, *remaining_label;
+       const char *err;
+       bool progress;
+       struct edge **edges = tal_arr(sub, struct edge *, 0);
+       size_t num_edges = 0;
+
+       /* We create canonical square, find texts. */
+       sq = square(sub, str, &texts);
+
+       /* Now search for arrows around each text, cleaning
+        * as we go! */
+       for (size_t i = 0; i < tal_count(texts); i++) {
+               struct xy perim;
+               enum dir pdir;
+               struct text *t = &texts[i];
+
+               for_each_perimeter(&perim, &pdir, t->xy, t->width) {
+                       struct edge *edge;
+                       err = try_create_edge(ctx, sq, texts, perim, t, add_node, arg, &edge);
+                       if (err)
+                               goto fail;
+                       if (edge) {
+                               tal_resize(&edges, num_edges+1);
+                               edges[num_edges++] = tal_steal(edges, edge);
+                       }
+               }
+       }
+
+       /* Now attach any remaining labels */
+       for (size_t i = 0; i < tal_count(texts); i++) {
+               struct text *t = &texts[i];
+
+               if (t->node)
+                       continue;
+               if (t->nearest_edge == &fake_edge) {
+                       err = tal_fmt(ctx, "Label at (%zu,%zu) near more than one edge",
+                                     t->xy.x-1, t->xy.y-1);
+                       goto fail;
+               }
+               if (t->nearest_edge)
+                       add_label(t->nearest_edge, t);
+       }
+
+       /* Any remaining labels must be attached to already-attached labels */
+       do {
+               progress = false;
+               remaining_label = NULL;
+
+               for (size_t i = 0; i < tal_count(texts); i++) {
+                       struct xy perim;
+                       enum dir pdir;
+                       struct text *t = &texts[i];
+
+                       if (t->node || t->nearest_edge)
+                               continue;
+
+                       remaining_label = t;
+                       for_each_perimeter(&perim, &pdir, t->xy, t->width) {
+                               struct text *neighbor = in_text(texts, perim);
+                               if (!neighbor || neighbor->node || !neighbor->nearest_edge)
+                                       continue;
+                               t->nearest_edge = neighbor->nearest_edge;
+                               add_label(t->nearest_edge, t);
+                               progress = true;
+                               break;
+                       }
+               }
+       } while (progress);
+
+       if (remaining_label) {
+               err = tal_fmt(ctx, "Label at (%zu,%zu) not near any edge",
+                             remaining_label->xy.x-1,
+                             remaining_label->xy.y-1);
+               goto fail;
+       }
+
+       err = scan_for_unused(ctx, texts, sq);
+       if (err)
+               goto fail;
+
+       /* Now add edges, complete with labels */
+       for (size_t i = 0; i < tal_count(edges); i++) {
+               err = add_edge(ctx, edges[i]->src->node, edges[i]->dst->node,
+                              edges[i]->bidir, edges[i]->labels, arg);
+               if (err)
+                       goto fail;
+       }
+       
+       tal_free(sub);
+       return NULL;
+
+fail:
+       tal_free(sub);
+       return err;
+}
diff --git a/ccan/ungraph/ungraph.h b/ccan/ungraph/ungraph.h
new file mode 100644 (file)
index 0000000..8480b35
--- /dev/null
@@ -0,0 +1,53 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_UNGRAPH_H
+#define CCAN_UNGRAPH_H
+#include <ccan/tal/tal.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
+
+/**
+ * ungraph: extract a graph from an ASCII graph.
+ * @ctx: context for callbacks, and/or returned errstr.
+ * @str: a string containing a graph.
+ * @add_node: callback for a new node, returns node.
+ * @add_edge: callback for a new edge, with tal_count(labels).
+ * @arg: callback argument.
+ *
+ * On success, returns NULL.  On failure, returns some error message
+ * (allocated off @ctx, or returned from callbacks).
+ *
+ * If @add_node returns NULL, it must set @errstr. @add_edge
+ * returns the error message directly.
+ *
+ * @add_node and @add_edge can tal_steal the name/labels if they want,
+ * otherwise they will be freed.
+ */
+const char *ungraph_(const tal_t *ctx,
+                    const char *str,
+                    void *(*add_node)(const tal_t *ctx,
+                                      const char *name,
+                                      const char **errstr,
+                                      void *arg),
+                    const char *(*add_edge)(const tal_t *ctx,
+                                            void *source_node,
+                                            void *dest_node,
+                                            bool bidir,
+                                            const char **labels,
+                                            void *arg),
+                    void *arg);
+
+#define ungraph(ctx, str, add_node, add_edge, arg)                     \
+       ungraph_((ctx), (str),                                          \
+                typesafe_cb_preargs(void *, void *,                    \
+                                    (add_node), (arg),                 \
+                                    const tal_t *,                     \
+                                    const char *,                      \
+                                    const char **errstr),              \
+                typesafe_cb_preargs(const char *, void *,              \
+                                    (add_edge), (arg),                 \
+                                    const tal_t *,                     \
+                                    void *,                            \
+                                    void *,                            \
+                                    bool,                              \
+                                    const char **),                    \
+                arg)
+#endif /* CCAN_UNGRAPH_H */
index 8820f17410293fcb9763b63272f41d5702d8ac13..9e805a3a387cc4c083aeaadce450c8baa53f854b 100644 (file)
@@ -55,7 +55,7 @@ static inline uint16_t version_minor(const struct version v) {
  */
 static inline struct version version(uint16_t major, uint16_t minor)
 {
-       struct version v = { ._v = major << 16 | minor };
+       struct version v = { ._v = (uint32_t)major << 16 | minor };
        return v;
 }
 
index f3d1a5ba13b36354d23bdf309ae28d8c75617bd3..3f88bbcdf830d0ca7afbd9e4214936e6940a1f9d 100644 (file)
@@ -151,11 +151,12 @@ static void reap_output(void)
                        int old_len, len;
                        /* This length includes nul terminator! */
                        old_len = tal_count(c->output);
-                       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;
index 57779f29905b68d82e537e8c4f0c49c2d282ebec..7d8f6b095de4b6e615591b6fb37319a20fe603a3 100644 (file)
@@ -197,7 +197,7 @@ static const struct test base_tests[] = {
          "return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
        { "HAVE_BUILTIN_CTZ", "__builtin_ctz support",
          "INSIDE_MAIN", NULL, NULL,
-         "return __builtin_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;" },
@@ -471,7 +471,7 @@ static const struct test base_tests[] = {
          "#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"
@@ -797,12 +797,12 @@ static bool run_test(const char *cmd, const char *wrapper, struct test *test)
                /* We run INSIDE_MAIN tests for sanity checking. */
                if (strstr(test->style, "EXECUTE")
                    || strstr(test->style, "INSIDE_MAIN")) {
-                       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",
index da9ed0a3fad62bdd3330285ae67751ef8084c7d2..da3e00cd73ba7088981d7a40ad3b995c4ad48e2b 100644 (file)
@@ -96,10 +96,11 @@ char *read_config_header(const char *ccan_dir, bool verbose)
        char *config_header;
 
        config_header = grab_file(NULL, fname);
-       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++) {
@@ -129,5 +130,6 @@ char *read_config_header(const char *ccan_dir, bool verbose)
                                       fname, cflags);
                }
        }
+       tal_free(fname);
        return config_header;
 }