From: David Gibson Date: Sat, 1 Jun 2024 09:03:27 +0000 (+1000) Subject: bitmap: Avoid shadowing type name with parameter name X-Git-Url: http://git.ozlabs.org/?a=commitdiff_plain;ds=inline;h=HEAD;hp=21543f835855d3e1310a1a0fa2fe68b1ddd4013e;p=ccan bitmap: Avoid shadowing type name with parameter name '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 Signed-off-by: David Gibson --- diff --git a/Makefile b/Makefile index d53e89f4..875e99f2 100644 --- 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 diff --git a/ccan/antithread/alloc/alloc.c b/ccan/antithread/alloc/alloc.c index 1b36aa47..33201ab9 100644 --- a/ccan/antithread/alloc/alloc.c +++ b/ccan/antithread/alloc/alloc.c @@ -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); diff --git a/ccan/antithread/alloc/tiny.c b/ccan/antithread/alloc/tiny.c index ffd17c65..d84974ee 100644 --- a/ccan/antithread/alloc/tiny.c +++ b/ccan/antithread/alloc/tiny.c @@ -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) diff --git a/ccan/base64/base64.c b/ccan/base64/base64.c index b2326293..c28e0da2 100644 --- a/ccan/base64/base64.c +++ b/ccan/base64/base64.c @@ -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]) diff --git a/ccan/base64/base64.h b/ccan/base64/base64.h index cef30d25..a899af4a 100644 --- a/ccan/base64/base64.h +++ b/ccan/base64/base64.h @@ -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); diff --git a/ccan/bitmap/bitmap.c b/ccan/bitmap/bitmap.c index d254b20e..ccc0cd21 100644 --- a/ccan/bitmap/bitmap.c +++ b/ccan/bitmap/bitmap.c @@ -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; diff --git a/ccan/bitmap/bitmap.h b/ccan/bitmap/bitmap.h index 54382801..e1bf4bb7 100644 --- a/ccan/bitmap/bitmap.h +++ b/ccan/bitmap/bitmap.h @@ -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_ */ diff --git a/ccan/bitops/test/run.c b/ccan/bitops/test/run.c index 5dba932d..6bb3acf5 100644 --- a/ccan/bitops/test/run.c +++ b/ccan/bitops/test/run.c @@ -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); diff --git a/ccan/bytestring/bytestring.h b/ccan/bytestring/bytestring.h index bc99e795..a0689db1 100644 --- a/ccan/bytestring/bytestring.h +++ b/ccan/bytestring/bytestring.h @@ -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 diff --git a/ccan/crypto/hmac_sha256/hmac_sha256.c b/ccan/crypto/hmac_sha256/hmac_sha256.c index 0392afe5..2238f9dc 100644 --- a/ccan/crypto/hmac_sha256/hmac_sha256.c +++ b/ccan/crypto/hmac_sha256/hmac_sha256.c @@ -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); /* diff --git a/ccan/darray/darray.h b/ccan/darray/darray.h index 58470fde..0b98fdac 100644 --- a/ccan/darray/darray.h +++ b/ccan/darray/darray.h @@ -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 { \ diff --git a/ccan/failtest/failtest.c b/ccan/failtest/failtest.c index c61ce442..205ded25 100644 --- a/ccan/failtest/failtest.c +++ b/ccan/failtest/failtest.c @@ -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? */ diff --git a/ccan/htable/htable.c b/ccan/htable/htable.c index cffd0619..f631ffeb 100644 --- a/ccan/htable/htable.c +++ b/ccan/htable/htable.c @@ -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) diff --git a/ccan/htable/htable.h b/ccan/htable/htable.h index eac57e37..faaf541b 100644 --- a/ccan/htable/htable.h +++ b/ccan/htable/htable.h @@ -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. diff --git a/ccan/htable/htable_type.h b/ccan/htable/htable_type.h index bb5ea086..0aacb7f3 100644 --- a/ccan/htable/htable_type.h +++ b/ccan/htable/htable_type.h @@ -159,8 +159,7 @@ 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 index 00000000..a4e5d38a --- /dev/null +++ b/ccan/htable/test/run-clash.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +/* 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(); +} diff --git a/ccan/htable/test/run-debug.c b/ccan/htable/test/run-debug.c index b9f48ee7..39991035 100644 --- a/ccan/htable/test/run-debug.c +++ b/ccan/htable/test/run-debug.c @@ -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)); diff --git a/ccan/htable/test/run.c b/ccan/htable/test/run.c index ada85f95..27007a41 100644 --- a/ccan/htable/test/run.c +++ b/ccan/htable/test/run.c @@ -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)); diff --git a/ccan/htable/tools/Makefile b/ccan/htable/tools/Makefile index a2cad59f..c8a428a7 100644 --- a/ccan/htable/tools/Makefile +++ b/ccan/htable/tools/Makefile @@ -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 index 00000000..5f7400b9 --- /dev/null +++ b/ccan/htable/tools/density.c @@ -0,0 +1,107 @@ +/* Density measurements for hashtables. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); + + 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; +} diff --git a/ccan/idtree/idtree.c b/ccan/idtree/idtree.c index 6e75450e..e10c1723 100644 --- a/ccan/idtree/idtree.c +++ b/ccan/idtree/idtree.c @@ -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])) { diff --git a/ccan/ilog/ilog.h b/ccan/ilog/ilog.h index 9adbb824..32702b17 100644 --- a/ccan/ilog/ilog.h +++ b/ccan/ilog/ilog.h @@ -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) diff --git a/ccan/io/fdpass/_info b/ccan/io/fdpass/_info index ba09025a..0b10e8a8 100644 --- a/ccan/io/fdpass/_info +++ b/ccan/io/fdpass/_info @@ -32,12 +32,20 @@ * 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. diff --git a/ccan/io/io.c b/ccan/io/io.c index 36dcb81e..12df06d8 100644 --- a/ccan/io/io.c +++ b/ccan/io/io.c @@ -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]; diff --git a/ccan/io/io.h b/ccan/io/io.h index e6905fb9..eeb5e36e 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -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 + * + * // 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 */ diff --git a/ccan/io/poll.c b/ccan/io/poll.c index 4cc9f4b7..634f83d2 100644 --- a/ccan/io/poll.c +++ b/ccan/io/poll.c @@ -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; +} diff --git a/ccan/mem/mem.h b/ccan/mem/mem.h index 19f69c03..20286dcb 100644 --- a/ccan/mem/mem.h +++ b/ccan/mem/mem.h @@ -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)); } /** diff --git a/ccan/membuf/_info b/ccan/membuf/_info index bdcbce2b..a859318c 100644 --- a/ccan/membuf/_info +++ b/ccan/membuf/_info @@ -26,13 +26,16 @@ * * 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; * } */ diff --git a/ccan/net/net.c b/ccan/net/net.c index 11c6b670..f57b8dd6 100644 --- a/ccan/net/net.c +++ b/ccan/net/net.c @@ -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; diff --git a/ccan/objset/_info b/ccan/objset/_info index 967764e7..116c2596 100644 --- a/ccan/objset/_info +++ b/ccan/objset/_info @@ -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 -. diff --git a/ccan/opt/helpers.c b/ccan/opt/helpers.c index 118e5436..df7ee6bb 100644 --- a/ccan/opt/helpers.c +++ b/ccan/opt/helpers.c @@ -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; } diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index d376a598..9149374c 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -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 { diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 6f4b9dda..e0331be2 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -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<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) { diff --git a/ccan/opt/test/run-add_desc.c b/ccan/opt/test/run-add_desc.c index b559c7f7..03e6986d 100644 --- a/ccan/opt/test/run-add_desc.c +++ b/ccan/opt/test/run-add_desc.c @@ -4,15 +4,24 @@ #include #include -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 " ". Fits, just. */ opt.show = NULL; opt.type = OPT_HASARG; diff --git a/ccan/opt/test/run-correct-reporting.c b/ccan/opt/test/run-correct-reporting.c index 8534f291..0c4f6c86 100644 --- a/ccan/opt/test/run-correct-reporting.c +++ b/ccan/opt/test/run-correct-reporting.c @@ -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(); diff --git a/ccan/opt/test/run-helpers.c b/ccan/opt/test/run-helpers.c index 0a08a85f..9aa41fe8 100644 --- a/ccan/opt/test/run-helpers.c +++ b/ccan/opt/test/run-helpers.c @@ -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] == '!'); } diff --git a/ccan/opt/test/run-set_alloc.c b/ccan/opt/test/run-set_alloc.c index 1dbb351b..2d7410ae 100644 --- a/ccan/opt/test/run-set_alloc.c +++ b/ccan/opt/test/run-set_alloc.c @@ -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 index 00000000..7f102f08 --- /dev/null +++ b/ccan/opt/test/run-userbits.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#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(); +} diff --git a/ccan/opt/test/utils.c b/ccan/opt/test/utils.c index 2ff04884..61199fb4 100644 --- a/ccan/opt/test/utils.c +++ b/ccan/opt/test/utils.c @@ -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; diff --git a/ccan/opt/test/utils.h b/ccan/opt/test/utils.h index 12cf0b75..3ada62d1 100644 --- a/ccan/opt/test/utils.h +++ b/ccan/opt/test/utils.h @@ -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[]; diff --git a/ccan/opt/usage.c b/ccan/opt/usage.c index 12f44a48..568e4661 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -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(" "); @@ -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; diff --git a/ccan/rbuf/rbuf.c b/ccan/rbuf/rbuf.c index d8d658d3..cc10cf3d 100644 --- a/ccan/rbuf/rbuf.c +++ b/ccan/rbuf/rbuf.c @@ -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 index 00000000..2354d129 --- /dev/null +++ b/ccan/rune/LICENSE @@ -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 index 00000000..2b2e2e8b --- /dev/null +++ b/ccan/rune/_info @@ -0,0 +1,130 @@ +#include "config.h" +#include +#include + +/** + * 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 + * 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 + * #include + * #include + * #include + * #include + * + * // 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 OR\n" + * "%s add OR\n" + * "%s test ", 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 index 00000000..495d37c3 --- /dev/null +++ b/ccan/rune/coding.c @@ -0,0 +1,426 @@ +/* MIT (BSD) license - see LICENSE file for details */ +/* Routines to encode / decode a rune */ +#include +#include +#include +#include +#include +#include +#include + +/* 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 index 00000000..e4de06cc --- /dev/null +++ b/ccan/rune/internal.h @@ -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 index 00000000..84296c66 --- /dev/null +++ b/ccan/rune/rune.c @@ -0,0 +1,491 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 index 00000000..c373269a --- /dev/null +++ b/ccan/rune/rune.h @@ -0,0 +1,400 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#ifndef CCAN_RUNE_RUNE_H +#define CCAN_RUNE_RUNE_H +#include +#include +#include +#include + +/* 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 '=' 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 "[|]*" + * @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 index 00000000..a37ee581 --- /dev/null +++ b/ccan/rune/test/run-alt-lexicographic-order.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +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 index 00000000..550cafae --- /dev/null +++ b/ccan/rune/test/run-altern-escape.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +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 index 00000000..d90b701c --- /dev/null +++ b/ccan/rune/test/run.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include + +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 index 00000000..880ea3c6 --- /dev/null +++ b/ccan/rune/test/test_vectors.csv @@ -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:f1v1,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= diff --git a/ccan/stringbuilder/stringbuilder.c b/ccan/stringbuilder/stringbuilder.c index d34de811..8eb4ab42 100644 --- a/ccan/stringbuilder/stringbuilder.c +++ b/ccan/stringbuilder/stringbuilder.c @@ -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); } diff --git a/ccan/strmap/strmap.c b/ccan/strmap/strmap.c index 9fa51d0d..16a30e03 100644 --- a/ccan/strmap/strmap.c +++ b/ccan/strmap/strmap.c @@ -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++) { diff --git a/ccan/strmap/strmap.h b/ccan/strmap/strmap.h index 0b32e6be..8724c31d 100644 --- a/ccan/strmap/strmap.h +++ b/ccan/strmap/strmap.h @@ -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. diff --git a/ccan/tal/tal.c b/ccan/tal/tal.c index 05d52e1d..1230d8ca 100644 --- a/ccan/tal/tal.c +++ b/ccan/tal/tal.c @@ -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**", p, p->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"); diff --git a/ccan/tal/tal.h b/ccan/tal/tal.h index a3a15491..c486f9e8 100644 --- a/ccan/tal/tal.h +++ b/ccan/tal/tal.h @@ -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); diff --git a/ccan/tal/test/run-notifier.c b/ccan/tal/test/run-notifier.c index 150f00ad..47e43640 100644 --- a/ccan/tal/test/run-notifier.c +++ b/ccan/tal/test/run-notifier.c @@ -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; diff --git a/ccan/tally/tally.c b/ccan/tally/tally.c index 29f05558..5cc3352a 100644 --- a/ccan/tally/tally.c +++ b/ccan/tally/tally.c @@ -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: diff --git a/ccan/tcon/tcon.h b/ccan/tcon/tcon.h index e0f84b38..df3aac88 100644 --- a/ccan/tcon/tcon.h +++ b/ccan/tcon/tcon.h @@ -147,8 +147,7 @@ * 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 *) diff --git a/ccan/tcon/test/compile_fail-container1.c b/ccan/tcon/test/compile_fail-container1.c index 44645a7e..ed1d3e20 100644 --- a/ccan/tcon/test/compile_fail-container1.c +++ b/ccan/tcon/test/compile_fail-container1.c @@ -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 diff --git a/ccan/tcon/test/compile_fail-container1w.c b/ccan/tcon/test/compile_fail-container1w.c index 19ba5bdc..a03f6514 100644 --- a/ccan/tcon/test/compile_fail-container1w.c +++ b/ccan/tcon/test/compile_fail-container1w.c @@ -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/tcon/test/compile_fail-container3.c b/ccan/tcon/test/compile_fail-container3.c index 9185225a..dfdfdba9 100644 --- a/ccan/tcon/test/compile_fail-container3.c +++ b/ccan/tcon/test/compile_fail-container3.c @@ -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 diff --git a/ccan/tcon/test/compile_fail-container3w.c b/ccan/tcon/test/compile_fail-container3w.c index 958e5c8b..a56e510f 100644 --- a/ccan/tcon/test/compile_fail-container3w.c +++ b/ccan/tcon/test/compile_fail-container3w.c @@ -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 index 00000000..2354d129 --- /dev/null +++ b/ccan/ungraph/LICENSE @@ -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 index 00000000..6088c5e4 --- /dev/null +++ b/ccan/ungraph/_info @@ -0,0 +1,77 @@ +#include "config.h" +#include +#include + +/** + * 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 + * #include + * #include + * + * // 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 + */ +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 index 00000000..a9ae9be5 --- /dev/null +++ b/ccan/ungraph/test/run.c @@ -0,0 +1,140 @@ +#include +/* Include the C files directly. */ +#include +#include +#include + +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 index 00000000..ba29d223 --- /dev/null +++ b/ccan/ungraph/ungraph.c @@ -0,0 +1,721 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#include +#include + +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 index 00000000..8480b359 --- /dev/null +++ b/ccan/ungraph/ungraph.h @@ -0,0 +1,53 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#ifndef CCAN_UNGRAPH_H +#define CCAN_UNGRAPH_H +#include +#include + +/** + * 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 */ diff --git a/ccan/version/version.h b/ccan/version/version.h index 8820f174..9e805a3a 100644 --- a/ccan/version/version.h +++ b/ccan/version/version.h @@ -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; } diff --git a/tools/ccanlint/async.c b/tools/ccanlint/async.c index f3d1a5ba..3f88bbcd 100644 --- a/tools/ccanlint/async.c +++ b/tools/ccanlint/async.c @@ -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; diff --git a/tools/configurator/configurator.c b/tools/configurator/configurator.c index 57779f29..7d8f6b09 100644 --- a/tools/configurator/configurator.c +++ b/tools/configurator/configurator.c @@ -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 \n" "#include \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", diff --git a/tools/read_config_header.c b/tools/read_config_header.c index da9ed0a3..da3e00cd 100644 --- a/tools/read_config_header.c +++ b/tools/read_config_header.c @@ -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; }