]> git.ozlabs.org Git - ccan/commitdiff
bitmap: Avoid shadowing type name with parameter name master
authorDavid Gibson <david@gibson.dropbear.id.au>
Sat, 1 Jun 2024 09:03:27 +0000 (19:03 +1000)
committerDavid Gibson <david@gibson.dropbear.id.au>
Sat, 1 Jun 2024 09:03:27 +0000 (19:03 +1000)
'bitmap' is a typedef name in this module, and we also use it as a
parameter name in a bunch of places.  Although that's possible, because
types and variables live in different namespaces, it's probably not that
wise.  It also appears to trip warnings with at least some options and
compiler versions.

Reported-by: Ashok Raj <ashok.raj@intel.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
74 files changed:
Makefile
ccan/antithread/alloc/alloc.c
ccan/antithread/alloc/tiny.c
ccan/base64/base64.c
ccan/base64/base64.h
ccan/bitmap/bitmap.c
ccan/bitmap/bitmap.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 d254b20e561ea804f456f777034c892d957e8cfd..ccc0cd21efa7247d3993dfb956b49608520019b7 100644 (file)
@@ -9,7 +9,7 @@
 #define BIT_ALIGN_DOWN(n)      ((n) & ~(BITMAP_WORD_BITS - 1))
 #define BIT_ALIGN_UP(n)                BIT_ALIGN_DOWN((n) + BITMAP_WORD_BITS - 1)
 
-void bitmap_zero_range(bitmap *bitmap, unsigned long n, unsigned long m)
+void bitmap_zero_range(bitmap *b, unsigned long n, unsigned long m)
 {
        unsigned long an = BIT_ALIGN_UP(n);
        unsigned long am = BIT_ALIGN_DOWN(m);
@@ -19,22 +19,22 @@ void bitmap_zero_range(bitmap *bitmap, unsigned long n, unsigned long m)
        assert(m >= n);
 
        if (am < an) {
-               BITMAP_WORD(bitmap, n) &= ~bitmap_bswap(headmask & tailmask);
+               BITMAP_WORD(b, n) &= ~bitmap_bswap(headmask & tailmask);
                return;
        }
 
        if (an > n)
-               BITMAP_WORD(bitmap, n) &= ~bitmap_bswap(headmask);
+               BITMAP_WORD(b, n) &= ~bitmap_bswap(headmask);
 
        if (am > an)
-               memset(&BITMAP_WORD(bitmap, an), 0,
+               memset(&BITMAP_WORD(b, an), 0,
                       (am - an) / BITMAP_WORD_BITS * sizeof(bitmap_word));
 
        if (m > am)
-               BITMAP_WORD(bitmap, m) &= ~bitmap_bswap(tailmask);
+               BITMAP_WORD(b, m) &= ~bitmap_bswap(tailmask);
 }
 
-void bitmap_fill_range(bitmap *bitmap, unsigned long n, unsigned long m)
+void bitmap_fill_range(bitmap *b, unsigned long n, unsigned long m)
 {
        unsigned long an = BIT_ALIGN_UP(n);
        unsigned long am = BIT_ALIGN_DOWN(m);
@@ -44,19 +44,19 @@ void bitmap_fill_range(bitmap *bitmap, unsigned long n, unsigned long m)
        assert(m >= n);
 
        if (am < an) {
-               BITMAP_WORD(bitmap, n) |= bitmap_bswap(headmask & tailmask);
+               BITMAP_WORD(b, n) |= bitmap_bswap(headmask & tailmask);
                return;
        }
 
        if (an > n)
-               BITMAP_WORD(bitmap, n) |= bitmap_bswap(headmask);
+               BITMAP_WORD(b, n) |= bitmap_bswap(headmask);
 
        if (am > an)
-               memset(&BITMAP_WORD(bitmap, an), 0xff,
+               memset(&BITMAP_WORD(b, an), 0xff,
                       (am - an) / BITMAP_WORD_BITS * sizeof(bitmap_word));
 
        if (m > am)
-               BITMAP_WORD(bitmap, m) |= bitmap_bswap(tailmask);
+               BITMAP_WORD(b, m) |= bitmap_bswap(tailmask);
 }
 
 static int bitmap_clz(bitmap_word w)
@@ -76,7 +76,7 @@ static int bitmap_clz(bitmap_word w)
 #endif
 }
 
-unsigned long bitmap_ffs(const bitmap *bitmap,
+unsigned long bitmap_ffs(const bitmap *b,
                         unsigned long n, unsigned long m)
 {
        unsigned long an = BIT_ALIGN_UP(n);
@@ -87,7 +87,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap,
        assert(m >= n);
 
        if (am < an) {
-               bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, n));
+               bitmap_word w = bitmap_bswap(BITMAP_WORD(b, n));
 
                w &= (headmask & tailmask);
 
@@ -95,7 +95,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap,
        }
 
        if (an > n) {
-               bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, n));
+               bitmap_word w = bitmap_bswap(BITMAP_WORD(b, n));
 
                w &= headmask;
 
@@ -104,7 +104,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap,
        }
 
        while (an < am) {
-               bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, an));
+               bitmap_word w = bitmap_bswap(BITMAP_WORD(b, an));
 
                if (w)
                        return an + bitmap_clz(w);
@@ -113,7 +113,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap,
        }
 
        if (m > am) {
-               bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, m));
+               bitmap_word w = bitmap_bswap(BITMAP_WORD(b, m));
 
                w &= tailmask;
 
index 543828010188aa39490005ebe53dcaba3b52c1a5..e1bf4bb761d236d63df247769f4d0239098c5657 100644 (file)
@@ -58,37 +58,37 @@ static inline bitmap_word bitmap_bswap(bitmap_word w)
 #define BITMAP_TAIL(_bm, _nbits) \
        (BITMAP_TAILWORD(_bm, _nbits) & BITMAP_TAILBITS(_nbits))
 
-static inline void bitmap_set_bit(bitmap *bitmap, unsigned long n)
+static inline void bitmap_set_bit(bitmap *b, unsigned long n)
 {
-       BITMAP_WORD(bitmap, n) |= BITMAP_WORDBIT(n);
+       BITMAP_WORD(b, n) |= BITMAP_WORDBIT(n);
 }
 
-static inline void bitmap_clear_bit(bitmap *bitmap, unsigned long n)
+static inline void bitmap_clear_bit(bitmap *b, unsigned long n)
 {
-       BITMAP_WORD(bitmap, n) &= ~BITMAP_WORDBIT(n);
+       BITMAP_WORD(b, n) &= ~BITMAP_WORDBIT(n);
 }
 
-static inline void bitmap_change_bit(bitmap *bitmap, unsigned long n)
+static inline void bitmap_change_bit(bitmap *b, unsigned long n)
 {
-       BITMAP_WORD(bitmap, n) ^= BITMAP_WORDBIT(n);
+       BITMAP_WORD(b, n) ^= BITMAP_WORDBIT(n);
 }
 
-static inline bool bitmap_test_bit(const bitmap *bitmap, unsigned long n)
+static inline bool bitmap_test_bit(const bitmap *b, unsigned long n)
 {
-       return !!(BITMAP_WORD(bitmap, n) & BITMAP_WORDBIT(n));
+       return !!(BITMAP_WORD(b, n) & BITMAP_WORDBIT(n));
 }
 
-void bitmap_zero_range(bitmap *bitmap, unsigned long n, unsigned long m);
-void bitmap_fill_range(bitmap *bitmap, unsigned long n, unsigned long m);
+void bitmap_zero_range(bitmap *b, unsigned long n, unsigned long m);
+void bitmap_fill_range(bitmap *b, unsigned long n, unsigned long m);
 
-static inline void bitmap_zero(bitmap *bitmap, unsigned long nbits)
+static inline void bitmap_zero(bitmap *b, unsigned long nbits)
 {
-       memset(bitmap, 0, bitmap_sizeof(nbits));
+       memset(b, 0, bitmap_sizeof(nbits));
 }
 
-static inline void bitmap_fill(bitmap *bitmap, unsigned long nbits)
+static inline void bitmap_fill(bitmap *b, unsigned long nbits)
 {
-       memset(bitmap, 0xff, bitmap_sizeof(nbits));
+       memset(b, 0xff, bitmap_sizeof(nbits));
 }
 
 static inline void bitmap_copy(bitmap *dst, const bitmap *src,
@@ -161,37 +161,36 @@ static inline bool bitmap_subset(const bitmap *src1, const bitmap *src2,
        return true;
 }
 
-static inline bool bitmap_full(const bitmap *bitmap, unsigned long nbits)
+static inline bool bitmap_full(const bitmap *b, unsigned long nbits)
 {
        unsigned long i;
 
        for (i = 0; i < BITMAP_HEADWORDS(nbits); i++) {
-               if (bitmap[i].w != -1UL)
+               if (b[i].w != -1UL)
                        return false;
        }
        if (BITMAP_HASTAIL(nbits) &&
-           (BITMAP_TAIL(bitmap, nbits) != BITMAP_TAILBITS(nbits)))
+           (BITMAP_TAIL(b, nbits) != BITMAP_TAILBITS(nbits)))
                return false;
 
        return true;
 }
 
-static inline bool bitmap_empty(const bitmap *bitmap, unsigned long nbits)
+static inline bool bitmap_empty(const bitmap *b, unsigned long nbits)
 {
        unsigned long i;
 
        for (i = 0; i < BITMAP_HEADWORDS(nbits); i++) {
-               if (bitmap[i].w != 0)
+               if (b[i].w != 0)
                        return false;
        }
-       if (BITMAP_HASTAIL(nbits) && (BITMAP_TAIL(bitmap, nbits) != 0))
+       if (BITMAP_HASTAIL(nbits) && (BITMAP_TAIL(b, nbits) != 0))
                return false;
 
        return true;
 }
 
-unsigned long bitmap_ffs(const bitmap *bitmap,
-                        unsigned long n, unsigned long m);
+unsigned long bitmap_ffs(const bitmap *b, unsigned long n, unsigned long m);
 
 /*
  * Allocation functions
@@ -221,26 +220,26 @@ static inline bitmap *bitmap_alloc1(unsigned long nbits)
        return bitmap;
 }
 
-static inline bitmap *bitmap_realloc0(bitmap *bitmap,
+static inline bitmap *bitmap_realloc0(bitmap *b,
                                      unsigned long obits, unsigned long nbits)
 {
-       bitmap = realloc(bitmap, bitmap_sizeof(nbits));
+       b = realloc(b, bitmap_sizeof(nbits));
 
-       if ((nbits > obits) && bitmap)
-               bitmap_zero_range(bitmap, obits, nbits);
+       if ((nbits > obits) && b)
+               bitmap_zero_range(b, obits, nbits);
 
-       return bitmap;
+       return b;
 }
 
-static inline bitmap *bitmap_realloc1(bitmap *bitmap,
+static inline bitmap *bitmap_realloc1(bitmap *b,
                                      unsigned long obits, unsigned long nbits)
 {
-       bitmap = realloc(bitmap, bitmap_sizeof(nbits));
+       b = realloc(b, bitmap_sizeof(nbits));
 
-       if ((nbits > obits) && bitmap)
-               bitmap_fill_range(bitmap, obits, nbits);
+       if ((nbits > obits) && b)
+               bitmap_fill_range(b, obits, nbits);
 
-       return bitmap;
+       return b;
 }
 
 #endif /* CCAN_BITMAP_H_ */
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;
 }