unsigned long arrsize = free_array_size(poolsize);
unsigned char *arr = pool;
unsigned long len, off, hdrlen;
- unsigned long i, freearr[arrsize], num_freearr = 0;
+ /* Don't have sanitizer complain here if arrsize is 0! */
+ unsigned long i, freearr[arrsize ? arrsize : 1], num_freearr = 0;
bool free;
if (poolsize < MIN_BLOCK_SIZE)
int8_t ret;
ret = maps->decode_map[(unsigned char)b64letter];
- if (ret == (char)0xff) {
+ if (ret == '\xff') {
errno = EDOM;
return -1;
}
bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char)
{
- return (maps->decode_map[(const unsigned char)b64char] != (char)0xff);
+ return (maps->decode_map[(const unsigned char)b64char] != '\xff');
}
void base64_init_maps(base64_maps_t *dest, const char src[64])
plan_tests(68 + 6 * (31 + 63));
for (i = 0; i < 32; i++)
- ok1(bitops_ffs32(1 << i) == i+1);
+ ok1(bitops_ffs32(1U << i) == i+1);
ok1(bitops_ffs32(0) == 0);
for (i = 0; i < 64; i++)
ok1(bitops_ffs64((uint64_t)1 << i) == i+1);
ok1(bitops_ffs64(0) == 0);
for (i = 0; i < 32; i++)
- ok1(bitops_clz32(1 << i) == 31 - i);
+ ok1(bitops_clz32(1U << i) == 31 - i);
for (i = 0; i < 64; i++)
ok1(bitops_clz64((uint64_t)1 << i) == 63 - i);
/* Lower bits don't effect results */
for (i = 0; i < 32; i++)
- ok1(bitops_clz32((1 << i) + (1 << i)-1) == 31 - i);
+ ok1(bitops_clz32((1U << i) + (1U << i)-1) == 31 - i);
for (i = 0; i < 64; i++)
ok1(bitops_clz64(((uint64_t)1 << i) + ((uint64_t)1 << i)-1)
== 63 - i);
for (i = 0; i < 32; i++)
- ok1(bitops_ctz32(1 << i) == i);
+ ok1(bitops_ctz32(1U << i) == i);
for (i = 0; i < 64; i++)
ok1(bitops_ctz64((uint64_t)1 << i) == i);
static inline struct bytestring bytestring_bytestring(struct bytestring haystack,
struct bytestring needle)
{
- const char *p = memmem(haystack.ptr, haystack.len,
- needle.ptr, needle.len);
+ const char *p;
+
+ /* Allow needle.ptr == NULL, without memmem sanitizer complaining */
+ if (needle.len == 0)
+ return bytestring(haystack.ptr, 0);
+
+ p = memmem(haystack.ptr, haystack.len, needle.ptr, needle.len);
if (p)
return bytestring(p, needle.len);
else
* (e.g., if K is of length 20 bytes and B=64, then K will be
* appended with 44 zero bytes 0x00)
*/
- memcpy(k_ipad, k, ksize);
+ if (ksize != 0)
+ memcpy(k_ipad, k, ksize);
memset((char *)k_ipad + ksize, 0, HMAC_SHA256_BLOCKSIZE - ksize);
/*
#define darray_append_items(arr, items, count) do { \
size_t count_ = (count), oldSize_ = (arr).size; \
- darray_resize(arr, oldSize_ + count_); \
- memcpy((arr).item + oldSize_, items, count_ * sizeof(*(arr).item)); \
+ /* Don't memcpy NULL! */ \
+ if (count_) { \
+ darray_resize(arr, oldSize_ + count_); \
+ memcpy((arr).item + oldSize_, items, count_ * sizeof(*(arr).item)); \
+ } \
} while(0)
#define darray_prepend_items(arr, items, count) do { \
size_t count_ = (count), oldSize_ = (arr).size; \
darray_resize(arr, count_ + oldSize_); \
- memmove((arr).item + count_, (arr).item, oldSize_ * sizeof(*(arr).item)); \
- memcpy((arr).item, items, count_ * sizeof(*(arr).item)); \
+ /* Don't memcpy NULL! */ \
+ if (count_) { \
+ memmove((arr).item + count_, (arr).item, oldSize_ * sizeof(*(arr).item)); \
+ memcpy((arr).item, items, count_ * sizeof(*(arr).item)); \
+ } \
} while(0)
#define darray_append_items_nullterminate(arr, items, count) do { \
call->line = line;
call->cleanup = NULL;
call->backtrace = get_backtrace(&call->backtrace_num);
- memcpy(&call->u, elem, elem_size);
+ if (elem_size != 0)
+ memcpy(&call->u, elem, elem_size);
tlist_add_tail(&history, call, list);
return call;
}
int failtest_pipe(int pipefd[2], const char *file, unsigned line)
{
struct failtest_call *p;
- struct pipe_call call;
- p = add_history(FAILTEST_PIPE, true, file, line, &call);
+ p = add_history_(FAILTEST_PIPE, true, file, line, NULL, 0);
if (should_fail(p)) {
p->u.open.ret = -1;
/* FIXME: Play with error codes? */
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) \
#define MAX_LEVEL (MAX_ID_SHIFT + IDTREE_BITS - 1) / IDTREE_BITS
#define IDTREE_FREE_MAX MAX_LEVEL + MAX_LEVEL
-#define set_bit(bit, v) (v) |= (1<<(bit))
-#define clear_bit(bit, v) (v) &= ~(1<<(bit))
-#define test_bit(bit, v) ((v) & (1<<(bit)))
+#define set_bit(bit, v) (v) |= (1U<<(bit))
+#define clear_bit(bit, v) (v) &= ~(1U<<(bit))
+#define test_bit(bit, v) ((v) & (1U<<(bit)))
struct idtree_layer {
uint32_t bitmap;
/* no space available go back to previous layer. */
l++;
oid = id;
- id = (id | ((1 << (IDTREE_BITS*l))-1)) + 1;
+ id = (id | ((1U << (IDTREE_BITS*l))-1)) + 1;
/* if already at the top layer, we need to grow */
if (!(p = pa[l])) {
#endif
#ifdef builtin_ilog32_nz
-#define ilog32(_v) (builtin_ilog32_nz(_v)&-!!(_v))
+/* This used to be builtin_ilog32_nz(_v)&-!!(_v), which means it zeroes out
+ * the undefined builtin_ilog32_nz(0) return. But clang UndefinedBehaviorSantizer
+ * complains, so do the branch: */
+#define ilog32(_v) ((_v) ? builtin_ilog32_nz(_v) : 0)
#define ilog32_nz(_v) builtin_ilog32_nz(_v)
#else
#define ilog32_nz(_v) ilog32(_v)
#endif /* builtin_ilog32_nz */
#ifdef builtin_ilog64_nz
-#define ilog64(_v) (builtin_ilog64_nz(_v)&-!!(_v))
+#define ilog32(_v) ((_v) ? builtin_ilog32_nz(_v) : 0)
#define ilog64_nz(_v) builtin_ilog64_nz(_v)
#else
#define ilog64_nz(_v) ilog64(_v)
* read_more, buf);
* }
*
+ * // Clean up allocation so -fsanitize=address doesn't see leak!
+ * static void free_buf(struct io_conn *c, struct buf *buf)
+ * {
+ * free(buf);
+ * }
+ *
* // Child has received fd, start reading loop.
* static struct io_plan *got_infd(struct io_conn *conn, int *infd)
* {
* struct buf *buf = calloc(1, sizeof(*buf));
+ * struct io_conn *new_conn;
*
- * io_new_conn(NULL, *infd, read_more, buf);
+ * new_conn = io_new_conn(NULL, *infd, read_more, buf);
+ * io_set_finish(new_conn, free_buf, buf);
* return io_close(conn);
* }
* // Child is receiving the fd to read into.
PURE_FUNCTION
static inline bool memeq(const void *a, size_t al, const void *b, size_t bl)
{
- return al == bl && !memcmp(a, b, bl);
+ return al == bl && (al == 0 || !memcmp(a, b, bl));
}
/**
*
* membuf_init(&charbuf, malloc(10), 10, membuf_realloc);
*
- * for (int i = 1; i < argc; i++)
- * strcpy(membuf_add(&charbuf, strlen(argv[i])), argv[i]);
+ * for (int i = 1; i < argc; i++) {
+ * size_t len = strlen(argv[i]);
+ * memcpy(membuf_add(&charbuf, len), argv[i], len);
+ * }
*
* // This is dumb, we could do all at once, but shows technique.
* while (membuf_num_elems(&charbuf) > 0)
* printf("%c", *(char *)membuf_consume(&charbuf, 1));
* printf("\n");
+ * free(membuf_cleanup(&charbuf));
* return 0;
* }
*/
* printf("%i,", i);
* printf("\n");
* }
+ * // Keep -fsanitize=address leak detection happy.
+ * objset_clear(&args);
* return 0;
* }
* // Given "a b c" outputs No arguments start with -.
return NULL;
}
-void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f)
+bool opt_show_floatval(char *buf, size_t len, const float *f)
{
double d = *f;
- opt_show_doubleval(buf, &d);
+ opt_show_doubleval(buf, len, &d);
+ return true;
}
char *opt_set_doubleval(const char *arg, double *d)
return NULL;
}
-void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d)
+bool opt_show_doubleval(char *buf, size_t len, const double *d)
{
- snprintf(buf, OPT_SHOW_LEN, "%f", *d);
+ snprintf(buf, len, "%f", *d);
+ return true;
}
char *opt_inc_intval(int *i)
exit(0);
}
-void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b)
+bool opt_show_bool(char *buf, size_t len, const bool *b)
{
- strncpy(buf, *b ? "true" : "false", OPT_SHOW_LEN);
+ strncpy(buf, *b ? "true" : "false", len);
+ return true;
}
-void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b)
+bool opt_show_invbool(char *buf, size_t len, const bool *b)
{
- strncpy(buf, *b ? "false" : "true", OPT_SHOW_LEN);
+ strncpy(buf, *b ? "false" : "true", len);
+ return true;
}
-void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p)
+bool opt_show_charp(char *buf, size_t len, char *const *p)
{
- if (*p){
- size_t len = strlen(*p);
+ if (*p) {
+ size_t plen = strlen(*p);
+ if (len < 2)
+ return false;
buf[0] = '"';
- if (len > OPT_SHOW_LEN - 2)
- len = OPT_SHOW_LEN - 2;
- strncpy(buf+1, *p, len);
- buf[1+len] = '"';
- if (len < OPT_SHOW_LEN - 2)
- buf[2+len] = '\0';
- }
- else {
- strncpy(buf, "(nil)", OPT_SHOW_LEN);
+ if (plen > len - 2)
+ plen = len - 2;
+ strncpy(buf+1, *p, plen);
+ buf[1+plen] = '"';
+ if (plen < len - 2)
+ buf[2+plen] = '\0';
+ return true;
+ } else {
+ return false;
}
}
/* Show an integer value, various forms. */
-void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i)
+bool opt_show_intval(char *buf, size_t len, const int *i)
{
- snprintf(buf, OPT_SHOW_LEN, "%i", *i);
+ snprintf(buf, len, "%i", *i);
+ return true;
}
-void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui)
+bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui)
{
- snprintf(buf, OPT_SHOW_LEN, "%u", *ui);
+ snprintf(buf, len, "%u", *ui);
+ return true;
}
-void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l)
+bool opt_show_longval(char *buf, size_t len, const long *l)
{
- snprintf(buf, OPT_SHOW_LEN, "%li", *l);
+ snprintf(buf, len, "%li", *l);
+ return true;
}
-void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul)
+bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul)
{
- snprintf(buf, OPT_SHOW_LEN, "%lu", *ul);
+ snprintf(buf, len, "%lu", *ul);
+ return true;
}
/* a helper function that multiplies out an argument's kMGTPE suffix in the
are separate but essentially identical functions for signed and unsigned
values, so that unsigned values greater than LLONG_MAX get suffixes.
*/
-static void show_llong_with_suffix(char buf[OPT_SHOW_LEN], long long ll,
- const long long base)
+static void show_llong_with_suffix(char *buf, size_t len, long long ll,
+ const long long base)
{
const char *suffixes = "kMGTPE";
int i;
if (ll == 0){
/*zero is special because everything divides it (you'd get "0E")*/
- snprintf(buf, OPT_SHOW_LEN, "0");
+ snprintf(buf, len, "0");
return;
}
for (i = 0; i < strlen(suffixes); i++){
ll = tmp;
}
if (i == 0)
- snprintf(buf, OPT_SHOW_LEN, "%"PRId64, (int64_t)ll);
+ snprintf(buf, len, "%"PRId64, (int64_t)ll);
else
- snprintf(buf, OPT_SHOW_LEN, "%"PRId64"%c", (int64_t)ll, suffixes[i - 1]);
+ snprintf(buf, len, "%"PRId64"%c", (int64_t)ll, suffixes[i - 1]);
}
-static void show_ullong_with_suffix(char buf[OPT_SHOW_LEN], unsigned long long ull,
+static void show_ullong_with_suffix(char *buf, size_t len,
+ unsigned long long ull,
const unsigned base)
{
const char *suffixes = "kMGTPE";
int i;
if (ull == 0){
/*zero is special because everything divides it (you'd get "0E")*/
- snprintf(buf, OPT_SHOW_LEN, "0");
+ snprintf(buf, len, "0");
return;
}
for (i = 0; i < strlen(suffixes); i++){
ull = tmp;
}
if (i == 0)
- snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, (uint64_t)ull);
+ snprintf(buf, len, "%"PRIu64, (uint64_t)ull);
else
- snprintf(buf, OPT_SHOW_LEN, "%"PRIu64"%c", (uint64_t)ull, suffixes[i - 1]);
+ snprintf(buf, len, "%"PRIu64"%c", (uint64_t)ull, suffixes[i - 1]);
}
/* _bi, signed */
-void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x)
+bool opt_show_intval_bi(char *buf, size_t len, const int *x)
{
- show_llong_with_suffix(buf, *x, 1024);
+ show_llong_with_suffix(buf, len, *x, 1024);
+ return true;
}
-void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x)
+bool opt_show_longval_bi(char *buf, size_t len, const long *x)
{
- show_llong_with_suffix(buf, *x, 1024);
+ show_llong_with_suffix(buf, len, *x, 1024);
+ return true;
}
-void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x)
+bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x)
{
- show_llong_with_suffix(buf, *x, 1024);
+ show_llong_with_suffix(buf, len, *x, 1024);
+ return true;
}
/* _bi, unsigned */
-void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x)
+bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x)
{
- show_ullong_with_suffix(buf, (unsigned long long) *x, 1024);
+ show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024);
+ return true;
}
-void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x)
+bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x)
{
- show_ullong_with_suffix(buf, (unsigned long long) *x, 1024);
+ show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024);
+ return true;
}
-void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x)
+bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x)
{
- show_ullong_with_suffix(buf, (unsigned long long) *x, 1024);
+ show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024);
+ return true;
}
/* _si, signed */
-void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x)
+bool opt_show_intval_si(char *buf, size_t len, const int *x)
{
- show_llong_with_suffix(buf, (long long) *x, 1000);
+ show_llong_with_suffix(buf, len, (long long) *x, 1000);
+ return true;
}
-void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x)
+bool opt_show_longval_si(char *buf, size_t len, const long *x)
{
- show_llong_with_suffix(buf, (long long) *x, 1000);
+ show_llong_with_suffix(buf, len, (long long) *x, 1000);
+ return true;
}
-void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x)
+bool opt_show_longlongval_si(char *buf, size_t len, const long long *x)
{
- show_llong_with_suffix(buf, *x, 1000);
+ show_llong_with_suffix(buf, len, *x, 1000);
+ return true;
}
/* _si, unsigned */
-void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x)
+bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x)
{
- show_ullong_with_suffix(buf, (unsigned long long) *x, 1000);
+ show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000);
+ return true;
}
-void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x)
+bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x)
{
- show_ullong_with_suffix(buf, (unsigned long long) *x, 1000);
+ show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000);
+ return true;
}
-void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x)
+bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x)
{
- show_ullong_with_suffix(buf, (unsigned long long) *x, 1000);
+ show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000);
+ return true;
}
static const char *first_opt(unsigned *i, unsigned *len)
{
for (*i = 0; *i < opt_count; (*i)++) {
- if (opt_table[*i].type == OPT_SUBTABLE)
+ if (opt_table[*i].type & OPT_SUBTABLE)
continue;
return first_name(opt_table[*i].names, len);
}
static const char *next_opt(const char *p, unsigned *i, unsigned *len)
{
for (; *i < opt_count; (*i)++) {
- if (opt_table[*i].type == OPT_SUBTABLE)
+ if (opt_table[*i].type & OPT_SUBTABLE)
continue;
if (!p)
return first_name(opt_table[*i].names, len);
{
const char *p;
unsigned len;
+ enum opt_type type = entry->type & (OPT_USER_MIN-1);
- if (entry->type != OPT_HASARG && entry->type != OPT_NOARG
- && entry->type != (OPT_EARLY|OPT_HASARG)
- && entry->type != (OPT_EARLY|OPT_NOARG))
+ if (type != OPT_HASARG && type != OPT_NOARG
+ && type != (OPT_EARLY|OPT_HASARG)
+ && type != (OPT_EARLY|OPT_NOARG))
failmsg("Option %s: unknown entry type %u",
entry->names, entry->type);
void _opt_register(const char *names, enum opt_type type,
char *(*cb)(void *arg),
char *(*cb_arg)(const char *optarg, void *arg),
- void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
+ bool (*show)(char *buf, size_t len, const void *arg),
const void *arg, const char *desc)
{
struct opt_table opt;
int found = -1, i;
for (i = 0; i < opt_count; i++) {
- if (opt_table[i].type == OPT_SUBTABLE)
+ if (opt_table[i].type & OPT_SUBTABLE)
continue;
if (strcmp(opt_table[i].names, names) == 0)
found = i;
add_opt(&heading);
}
for (i = 0; entry[i].type != OPT_END; i++) {
- if (entry[i].type == OPT_SUBTABLE)
+ if (entry[i].type & OPT_SUBTABLE)
opt_register_table(subtable_of(&entry[i]),
entry[i].desc);
else {
* where "type" is the type of the @arg argument. The first argument to the
* @cb is the argument found on the commandline.
*
- * Similarly, if @show is not NULL, it should be of type "void *show(char *,
- * const type *)". It should write up to OPT_SHOW_LEN bytes into the first
- * argument; unless it uses the entire OPT_SHOW_LEN bytes it should
- * nul-terminate that buffer.
+ * Similarly, if @show is not NULL, it should be of type "bool show(char *,
+ * size_t len, const type *)". If there is no default, it should return false,
+ * otherwise it should write up to len bytes into the first argument and
+ * return true; unless it uses the entire len bytes it should nul-terminate that
+ * buffer.
*
* Any number of equivalent short or long options can be listed in @names,
* separated by '|'. Short options are a single hyphen followed by a single
*/
extern const char opt_hidden[];
-/* Maximum length of arg to show in opt_usage */
-#define OPT_SHOW_LEN 80
-
/* Standard helpers. You can write your own: */
/* Sets the @b to true. */
char *opt_set_bool(bool *b);
/* Sets @b based on arg: (yes/no/true/false). */
char *opt_set_bool_arg(const char *arg, bool *b);
-void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b);
+bool opt_show_bool(char *buf, size_t len, const bool *b);
/* The inverse */
char *opt_set_invbool(bool *b);
-void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b);
+bool opt_show_invbool(char *buf, size_t len, const bool *b);
/* Sets @b based on !arg: (yes/no/true/false). */
char *opt_set_invbool_arg(const char *arg, bool *b);
/* Set a char *. */
char *opt_set_charp(const char *arg, char **p);
-void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p);
+/* If *p is NULL, this returns false (i.e. doesn't show a default) */
+bool opt_show_charp(char *buf, size_t len, char *const *p);
/* Set an integer value, various forms. Sets to 1 on arg == NULL. */
char *opt_set_intval(const char *arg, int *i);
-void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i);
+bool opt_show_intval(char *buf, size_t len, const int *i);
char *opt_set_uintval(const char *arg, unsigned int *ui);
-void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui);
+bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui);
char *opt_set_longval(const char *arg, long *l);
-void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l);
+bool opt_show_longval(char *buf, size_t len, const long *l);
char *opt_set_ulongval(const char *arg, unsigned long *ul);
-void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul);
+bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul);
/* Set an floating point value, various forms. */
char *opt_set_floatval(const char *arg, float *f);
-void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f);
+bool opt_show_floatval(char *buf, size_t len, const float *f);
char *opt_set_doubleval(const char *arg, double *d);
-void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d);
+bool opt_show_doubleval(char *buf, size_t len, const double *d);
/* the following setting functions accept k, M, G, T, P, or E suffixes, which
multiplies the numeric value by the corresponding power of 1000 or 1024
char *opt_set_ulonglongval_si(const char *arg, unsigned long long *ll);
-void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x);
-void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x);
-void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x);
-void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x);
-void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x);
-void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x);
+bool opt_show_intval_bi(char *buf, size_t len, const int *x);
+bool opt_show_longval_bi(char *buf, size_t len, const long *x);
+bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x);
+bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x);
+bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x);
+bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x);
-void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x);
-void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x);
-void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x);
-void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x);
-void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x);
-void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x);
+bool opt_show_intval_si(char *buf, size_t len, const int *x);
+bool opt_show_longval_si(char *buf, size_t len, const long *x);
+bool opt_show_longlongval_si(char *buf, size_t len, const long long *x);
+bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x);
+bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x);
+bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x);
/* Display usage string to stdout, exit(0). */
char *opt_usage_and_exit(const char *extra);
+/**
+ * opt_find_long: low-level access to the parser
+ * @arg: string of form 'arg' or 'arg=val'.
+ * @optarg: set to `val` of present in arg, otherwise NULL. Can be NULL.
+ *
+ * Returns NULL if option is unknown. Sets *@optarg to NULL if
+ * there's no '='.
+ */
+struct opt_table *opt_find_long(const char *arg, const char **optarg);
+
+/**
+ * opt_find_short: low-level access to the parser
+ * @arg: character representing short option
+ *
+ * Returns NULL if option is unknown.
+ */
+struct opt_table *opt_find_short(char arg);
+
+/* opt_type bits reserved for users to play with (ignored!).
+ * You can set bits in type e.g. (1<<OPT_USER_START) to (1<<OPT_USER_END)
+ * when calling _opt_register. */
+#define OPT_USER_START 8
+#define OPT_USER_END 15
+
/* Below here are private declarations. */
/* You can use this directly to build tables, but the macros will ensure
* consistency and type safety. */
OPT_SUBTABLE = 4, /* Actually, longopt points to a subtable... */
OPT_EARLY = 8, /* Parse this from opt_early_parse() only. */
OPT_END = 16, /* End of the table. */
+
+ /* Make sure no compiler will assume we never have large
+ * values in the enum! */
+ OPT_USER_MIN = (1 << OPT_USER_START),
+ OPT_USER_MAX = (1 << OPT_USER_END),
};
struct opt_table {
enum opt_type type;
char *(*cb)(void *arg); /* OPT_NOARG */
char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */
- void (*show)(char buf[OPT_SHOW_LEN], const void *arg);
+ bool (*show)(char *buf, size_t len, const void *arg);
union {
const void *carg;
void *arg;
char *(*)(const char *, const typeof(*(arg))*), \
char *(*)(const char *, const void *), \
(cb)), \
- typesafe_cb_cast(void (*)(char buf[], const void *), \
- void (*)(char buf[], const typeof(*(arg))*), (show))
+ typesafe_cb_cast(bool (*)(char *buf, size_t, const void *), \
+ bool (*)(char *buf, size_t, const typeof(*(arg))*), (show))
/* Non-typesafe register function. */
void _opt_register(const char *names, enum opt_type type,
char *(*cb)(void *arg),
char *(*cb_arg)(const char *optarg, void *arg),
- void (*show)(char buf[OPT_SHOW_LEN], const void *arg),
+ bool (*show)(char *buf, size_t len, const void *arg),
const void *arg, const char *desc);
/* We use this to get typechecking for OPT_SUBTABLE */
/tmp/opt-example: option requires an argument -- 's'
*/
static int parse_err(void (*errlog)(const char *fmt, ...),
- const char *argv0, const char *arg, unsigned len,
+ const char *argv0,
+ const char *arg, unsigned len,
const char *problem)
{
errlog("%s: %.*s: %s", argv0, len, arg, problem);
(*argc)--;
}
+/* This sets the len and o to indicate how far it is into the
+ * opt_table's names field. */
+static struct opt_table *opt_find_long_extra(const char *arg,
+ const char **optarg,
+ unsigned int *len,
+ const char **o)
+{
+ unsigned i;
+
+ *optarg = NULL;
+ for (*o = first_lopt(&i, len);
+ *o;
+ *o = next_lopt(*o, &i, len)) {
+ if (strncmp(arg, *o, *len) != 0)
+ continue;
+ if (arg[*len] == '=')
+ *optarg = arg + *len + 1;
+ else if (arg[*len] != '\0')
+ continue;
+ return &opt_table[i];
+
+ }
+ return NULL;
+}
+
+struct opt_table *opt_find_long(const char *arg, const char **optarg)
+{
+ unsigned len;
+ const char *o;
+
+ return opt_find_long_extra(arg, optarg ? optarg : &o, &len, &o);
+}
+
+static struct opt_table *opt_find_short_extra(char arg, const char **o)
+{
+ unsigned i;
+ for (*o = first_sopt(&i); *o; *o = next_sopt(*o, &i)) {
+ if (arg == **o)
+ return &opt_table[i];
+ }
+ return NULL;
+}
+
+struct opt_table *opt_find_short(char arg)
+{
+ const char *o;
+ return opt_find_short_extra(arg, &o);
+}
+
/* Returns 1 if argument consumed, 0 if all done, -1 on error. */
int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
void (*errlog)(const char *fmt, ...), bool unknown_ok)
{
- unsigned i, arg, len;
+ unsigned arg, len;
const char *o, *optarg = NULL;
char *problem = NULL;
+ struct opt_table *ot;
if (getenv("POSIXLY_CORRECT")) {
/* Don't find options after non-options. */
/* Long options start with -- */
if (argv[arg][1] == '-') {
assert(*offset == 0);
- for (o = first_lopt(&i, &len); o; o = next_lopt(o, &i, &len)) {
- if (strncmp(argv[arg] + 2, o, len) != 0)
- continue;
- if (argv[arg][2 + len] == '=')
- optarg = argv[arg] + 2 + len + 1;
- else if (argv[arg][2 + len] != '\0')
- continue;
- break;
- }
- if (!o) {
+
+ ot = opt_find_long_extra(argv[arg]+2, &optarg, &len, &o);
+ if (!ot) {
if (unknown_ok)
goto ok;
return parse_err(errlog, argv[0],
argv[arg], strlen(argv[arg]),
"unrecognized option");
}
+
/* For error messages, we include the leading '--' */
o -= 2;
len += 2;
} else {
- /* offset allows us to handle -abc */
- for (o = first_sopt(&i); o; o = next_sopt(o, &i)) {
- if (argv[arg][*offset + 1] != *o)
- continue;
- (*offset)++;
- break;
- }
- if (!o) {
+ ot = opt_find_short_extra(argv[arg][*offset + 1], &o);
+ if (!ot) {
if (unknown_ok) {
(*offset)++;
goto ok;
argv[arg], strlen(argv[arg]),
"unrecognized option");
}
+
+ (*offset)++;
/* For error messages, we include the leading '-' */
o--;
len = 2;
}
- if ((opt_table[i].type & ~OPT_EARLY) == OPT_NOARG) {
+ if (ot->type & OPT_NOARG) {
if (optarg)
return parse_err(errlog, argv[0], o, len,
"doesn't allow an argument");
- if ((opt_table[i].type & OPT_EARLY) == is_early)
- problem = opt_table[i].cb(opt_table[i].u.arg);
+ if ((ot->type & OPT_EARLY) == is_early)
+ problem = ot->cb(ot->u.arg);
} else {
if (!optarg) {
/* Swallow any short options as optarg, eg -afile */
if (!optarg)
return parse_err(errlog, argv[0], o, len,
"requires an argument");
- if ((opt_table[i].type & OPT_EARLY) == is_early)
- problem = opt_table[i].cb_arg(optarg,
- opt_table[i].u.arg);
+ if ((ot->type & OPT_EARLY) == is_early)
+ problem = ot->cb_arg(optarg, ot->u.arg);
}
if (problem) {
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
-static void show_10(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED)
+static bool show_10(char *buf, size_t len, const void *arg UNNEEDED)
{
memset(buf, 'X', 10);
buf[10] = '\0';
+ return true;
}
-static void show_max(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED)
+static bool show_10_false(char *buf, size_t len, const void *arg UNNEEDED)
+{
+ memset(buf, 'X', 10);
+ buf[10] = '\0';
+ return false;
+}
+
+static bool show_max(char *buf, size_t len, const void *arg UNNEEDED)
{
memset(buf, 'X', OPT_SHOW_LEN);
+ return true;
}
/* Test add_desc helper. */
char *ret;
size_t len, max;
- plan_tests(30);
+ plan_tests(32);
opt.show = NULL;
opt.names = "01234";
" (default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...)\n") == 0);
free(ret); len = max = 0;
+ /* With show function which fails doesn't print. */
+ opt.show = show_10_false;
+ ret = add_desc(NULL, &len, &max, 7, 41, &opt);
+ ok1(len < max);
+ ret[len] = '\0';
+ ok1(strcmp(ret, "01234 0123456789 0\n") == 0);
+ free(ret); len = max = 0;
+
/* With added " <arg>". Fits, just. */
opt.show = NULL;
opt.type = OPT_HASARG;
int main(int argc, char *argv[])
{
- plan_tests(12);
+ plan_tests(14);
/* --aaa without args. */
opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "");
free(err_output);
err_output = NULL;
+ opt_register_noarg("-d", test_noarg, NULL, "");
+ ok1(!parse_args(&argc, &argv, "-dc", NULL));
+ ok1(strstr(err_output, ": -c: requires an argument"));
+
/* parse_args allocates argv */
free(argv);
return exit_status();
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = -77;
- opt_show_intval_bi(buf, &i);
+ opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-77") == 0);
i = 0;
- opt_show_intval_bi(buf, &i);
+ opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "0") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 77;
- opt_show_intval_bi(buf, &i);
+ opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = -1234 * k;
- opt_show_intval_bi(buf, &i);
+ opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-1234k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_intval_bi(buf, &i);
+ opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1024 * M;
- opt_show_intval_bi(buf, &i);
+ opt_show_intval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1G") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = -77;
- opt_show_longval_bi(buf, &i);
+ opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 77;
- opt_show_longval_bi(buf, &i);
+ opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = -1 * k;
- opt_show_longval_bi(buf, &i);
+ opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-1k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_longval_bi(buf, &i);
+ opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1024 * M;
- opt_show_longval_bi(buf, &i);
+ opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1G") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 0;
- opt_show_longval_bi(buf, &i);
+ opt_show_longval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "0") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = -7777;
- opt_show_longlongval_bi(buf, &i);
+ opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-7777") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 7777;
- opt_show_longlongval_bi(buf, &i);
+ opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "7777") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = -10240000 * k;
- opt_show_longlongval_bi(buf, &i);
+ opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-10000M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 5 * P;
- opt_show_longlongval_bi(buf, &i);
+ opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "5P") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1024 * P;
- opt_show_longlongval_bi(buf, &i);
+ opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1E") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = 77;
- opt_show_uintval_bi(buf, &i);
+ opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1234 * k;
- opt_show_uintval_bi(buf, &i);
+ opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1234k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_uintval_bi(buf, &i);
+ opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1024 * M;
- opt_show_uintval_bi(buf, &i);
+ opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1G") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = 77;
- opt_show_ulongval_bi(buf, &i);
+ opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = k;
- opt_show_ulongval_bi(buf, &i);
+ opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_ulongval_bi(buf, &i);
+ opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1024 * M;
- opt_show_ulongval_bi(buf, &i);
+ opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1G") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 0;
- opt_show_ulongval_bi(buf, &i);
+ opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "0") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = 7777;
- opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "7777") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 10240000 * k;
- opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "10000M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 5 * P;
- opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "5P") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1024 * P;
- opt_show_ulonglongval_bi(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "1E") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = -77;
- opt_show_intval_si(buf, &i);
+ opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-77") == 0);
i = 0;
- opt_show_intval_si(buf, &i);
+ opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "0") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 77;
- opt_show_intval_si(buf, &i);
+ opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = -1234 * k;
- opt_show_intval_si(buf, &i);
+ opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-1234k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_intval_si(buf, &i);
+ opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1000 * M;
- opt_show_intval_si(buf, &i);
+ opt_show_intval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1G") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = -77;
- opt_show_longval_si(buf, &i);
+ opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 77;
- opt_show_longval_si(buf, &i);
+ opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = -1 * k;
- opt_show_longval_si(buf, &i);
+ opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-1k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_longval_si(buf, &i);
+ opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1000 * M;
- opt_show_longval_si(buf, &i);
+ opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1G") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 0;
- opt_show_longval_si(buf, &i);
+ opt_show_longval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "0") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = -7777;
- opt_show_longlongval_si(buf, &i);
+ opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-7777") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 7777;
- opt_show_longlongval_si(buf, &i);
+ opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "7777") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = -10240000 * k;
- opt_show_longlongval_si(buf, &i);
+ opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-10240M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 5 * P;
- opt_show_longlongval_si(buf, &i);
+ opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "5P") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 2000 * P;
- opt_show_longlongval_si(buf, &i);
+ opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "2E") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = 77;
- opt_show_uintval_si(buf, &i);
+ opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1234 * k;
- opt_show_uintval_si(buf, &i);
+ opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1234k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_uintval_si(buf, &i);
+ opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1000 * M;
- opt_show_uintval_si(buf, &i);
+ opt_show_uintval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1G") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = 77;
- opt_show_ulongval_si(buf, &i);
+ opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = k;
- opt_show_ulongval_si(buf, &i);
+ opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1k") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 500 * M;
- opt_show_ulongval_si(buf, &i);
+ opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "500M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1024 * M;
- opt_show_ulongval_si(buf, &i);
+ opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "1024M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 0;
- opt_show_ulongval_si(buf, &i);
+ opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "0") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
char buf[OPT_SHOW_LEN+2] = { 0 };
buf[OPT_SHOW_LEN] = '!';
i = 7777;
- opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "7777") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 10240000 * k;
- opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "10240M") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 5 * P;
- opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "5P") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 1000 * P;
- opt_show_ulonglongval_si(buf, (unsigned long long *)&i);
+ opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i);
ok1(strcmp(buf, "1E") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
buf[OPT_SHOW_LEN] = '!';
b = true;
- opt_show_bool(buf, &b);
+ opt_show_bool(buf, OPT_SHOW_LEN, &b);
ok1(strcmp(buf, "true") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
b = false;
- opt_show_bool(buf, &b);
+ opt_show_bool(buf, OPT_SHOW_LEN, &b);
ok1(strcmp(buf, "false") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
buf[OPT_SHOW_LEN] = '!';
b = true;
- opt_show_invbool(buf, &b);
+ opt_show_invbool(buf, OPT_SHOW_LEN, &b);
ok1(strcmp(buf, "false") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
b = false;
- opt_show_invbool(buf, &b);
+ opt_show_invbool(buf, OPT_SHOW_LEN, &b);
ok1(strcmp(buf, "true") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
/* Short test. */
p = str;
strcpy(p, "short");
- opt_show_charp(buf, &p);
+ opt_show_charp(buf, OPT_SHOW_LEN, &p);
ok1(strcmp(buf, "\"short\"") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
/* Truncate test. */
memset(p, 'x', OPT_SHOW_LEN*2);
p[OPT_SHOW_LEN*2-1] = '\0';
- opt_show_charp(buf, &p);
+ opt_show_charp(buf, OPT_SHOW_LEN, &p);
ok1(buf[0] == '"');
ok1(buf[OPT_SHOW_LEN-1] == '"');
ok1(buf[OPT_SHOW_LEN] == '!');
buf[OPT_SHOW_LEN] = '!';
i = -77;
- opt_show_intval(buf, &i);
+ opt_show_intval(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "-77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
i = 77;
- opt_show_intval(buf, &i);
+ opt_show_intval(buf, OPT_SHOW_LEN, &i);
ok1(strcmp(buf, "77") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
buf[OPT_SHOW_LEN] = '!';
ui = 4294967295U;
- opt_show_uintval(buf, &ui);
+ opt_show_uintval(buf, OPT_SHOW_LEN, &ui);
ok1(strcmp(buf, "4294967295") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
buf[OPT_SHOW_LEN] = '!';
l = 1234567890L;
- opt_show_longval(buf, &l);
+ opt_show_longval(buf, OPT_SHOW_LEN, &l);
ok1(strcmp(buf, "1234567890") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
buf[OPT_SHOW_LEN] = '!';
ul = 4294967295UL;
- opt_show_ulongval(buf, &ul);
+ opt_show_ulongval(buf, OPT_SHOW_LEN, &ul);
ok1(strcmp(buf, "4294967295") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
buf[OPT_SHOW_LEN] = '!';
f = -77.5;
- opt_show_floatval(buf, &f);
+ opt_show_floatval(buf, OPT_SHOW_LEN, &f);
ok1(strcmp(buf, "-77.500000") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
f = 77.5;
- opt_show_floatval(buf, &f);
+ opt_show_floatval(buf, OPT_SHOW_LEN, &f);
ok1(strcmp(buf, "77.500000") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
buf[OPT_SHOW_LEN] = '!';
d = -77;
- opt_show_doubleval(buf, &d);
+ opt_show_doubleval(buf, OPT_SHOW_LEN, &d);
ok1(strcmp(buf, "-77.000000") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
d = 77;
- opt_show_doubleval(buf, &d);
+ opt_show_doubleval(buf, OPT_SHOW_LEN, &d);
ok1(strcmp(buf, "77.000000") == 0);
ok1(buf[OPT_SHOW_LEN] == '!');
}
static void freefn(void *ptr)
{
free_count++;
- free(ptr);
*find_ptr(ptr) = NULL;
+ free(ptr);
}
int main(int argc, char *argv[])
--- /dev/null
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+int main(int argc, char *argv[])
+{
+ const char *myname = argv[0];
+
+ plan_tests(28);
+
+ opt_register_noarg("-a", test_noarg, NULL, "All");
+ opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll");
+ opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "AAAAAAll");
+
+ ok1(strcmp(opt_table[0].names, "-a") == 0);
+ ok1(opt_table[0].type == OPT_NOARG);
+ ok1(strcmp(opt_table[1].names, "--aaa") == 0);
+ ok1(opt_table[1].type == OPT_NOARG);
+ ok1(strcmp(opt_table[2].names, "-b|--bbb") == 0);
+ ok1(opt_table[2].type == OPT_HASARG);
+
+ opt_table[0].type |= (1 << OPT_USER_START);
+ opt_table[1].type |= ((1 << OPT_USER_END)-1) - ((1 << OPT_USER_START)-1);
+ opt_table[2].type |= (1 << OPT_USER_END);
+
+ /* Should all work fine! */
+ ok1(parse_args(&argc, &argv, "-a", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(test_cb_called == 1);
+
+ ok1(parse_args(&argc, &argv, "--aaa", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(test_cb_called == 2);
+
+ /* This one needs an arg. */
+ ok1(parse_args(&argc, &argv, "-b", NULL) == false);
+ ok1(test_cb_called == 2);
+ ok1(parse_args(&argc, &argv, "-b", "bbb", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(argv[1] == NULL);
+ ok1(test_cb_called == 3);
+
+ ok1(parse_args(&argc, &argv, "--bbb", "bbb", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(argv[1] == NULL);
+ ok1(test_cb_called == 4);
+
+ /* parse_args allocates argv */
+ free(argv);
+ return exit_status();
+}
return NULL;
}
-void show_arg(char buf[OPT_SHOW_LEN], const char *arg)
+bool show_arg(char *buf, size_t len, const char *arg)
{
- strncpy(buf, arg, OPT_SHOW_LEN);
+ strncpy(buf, arg, len);
+ return true;
}
char *err_output = NULL;
extern unsigned int test_cb_called;
char *test_noarg(void *arg);
char *test_arg(const char *optarg, const char *arg);
-void show_arg(char buf[OPT_SHOW_LEN], const char *arg);
+bool show_arg(char *buf, size_t len, const char *arg);
extern struct opt_table short_table[];
extern struct opt_table long_table[];
#define MIN_DESC_WIDTH 40
#define MIN_TOTAL_WIDTH 50
+/* Maximum length of arg to show in opt_usage */
+#define OPT_SHOW_LEN 80
+
static unsigned int get_columns(void)
{
int ws_col = 0;
}
}
- *start = (words[oldlen - 1] == '\n');
+ if (oldlen != 0)
+ *start = (words[oldlen - 1] == '\n');
return oldlen;
}
if (opt->show) {
char buf[OPT_SHOW_LEN + sizeof("...")];
strcpy(buf + OPT_SHOW_LEN, "...");
- opt->show(buf, opt->u.arg);
+ if (opt->show(buf, OPT_SHOW_LEN, opt->u.arg)) {
+ /* If it doesn't fit on this line, indent. */
+ if (off + strlen(" (default: ") + strlen(buf) + strlen(")")
+ > width) {
+ base = add_indent(base, len, max, indent);
+ } else {
+ /* Remove \n. */
+ (*len)--;
+ }
- /* If it doesn't fit on this line, indent. */
- if (off + strlen(" (default: ") + strlen(buf) + strlen(")")
- > width) {
- base = add_indent(base, len, max, indent);
- } else {
- /* Remove \n. */
- (*len)--;
+ base = add_str(base, len, max, " (default: ");
+ base = add_str(base, len, max, buf);
+ base = add_str(base, len, max, ")\n");
}
-
- base = add_str(base, len, max, " (default: ");
- base = add_str(base, len, max, buf);
- base = add_str(base, len, max, ")\n");
}
return base;
}
size_t l;
if (opt_table[i].desc == opt_hidden)
continue;
- if (opt_table[i].type == OPT_SUBTABLE)
+ if (opt_table[i].type & OPT_SUBTABLE)
continue;
l = strlen(opt_table[i].names);
- if (opt_table[i].type == OPT_HASARG
+ if ((opt_table[i].type & OPT_HASARG)
&& !strchr(opt_table[i].names, ' ')
&& !strchr(opt_table[i].names, '='))
l += strlen(" <arg>");
for (i = 0; i < opt_count; i++) {
if (opt_table[i].desc == opt_hidden)
continue;
- if (opt_table[i].type == OPT_SUBTABLE) {
+ if (opt_table[i].type & OPT_SUBTABLE) {
ret = add_str(ret, &len, &max, opt_table[i].desc);
ret = add_str(ret, &len, &max, ":\n");
continue;
ssize_t r = 0;
size_t prev = 0;
- while (!(p = memchr(membuf_elems(&rbuf->m) + prev,
- term,
- membuf_num_elems(&rbuf->m) - prev))) {
+ /* memchr(NULL, ..., 0) is illegal. FML. */
+ while (membuf_num_elems(&rbuf->m) == prev
+ || !(p = memchr(membuf_elems(&rbuf->m) + prev,
+ term,
+ membuf_num_elems(&rbuf->m) - prev))) {
prev += r;
r = get_more(rbuf);
if (r < 0)
return true;
}
-static bool is_valid_cond(enum rune_condition cond)
+bool rune_condition_is_valid(enum rune_condition cond)
{
switch (cond) {
case RUNE_COND_IF_MISSING:
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);
- const char *strstart = *data;
char *value;
- size_t strlen = 0;
+ size_t strlen;
char c;
- /* Swallow field up to conditional */
- for (;;) {
- if (!pull_char(data, len, &c))
- return tal_free(alt);
- if (cispunct(c))
- break;
- strlen++;
- }
+ /* Swallow field up to possible conditional */
+ strlen = rune_altern_fieldname_len(*data, *len);
+ alt->fieldname = tal_strndup(alt, *data, strlen);
+ *data += strlen;
+ *len -= strlen;
- alt->fieldname = tal_strndup(alt, strstart, strlen);
- if (!is_valid_cond(c)) {
- pull_invalid(data, len);
+ /* Grab conditional */
+ if (!pull_char(data, len, &c) || !rune_condition_is_valid(c))
return tal_free(alt);
- }
+
alt->condition = c;
/* Assign worst case. */
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 */
assert(vecs);
lines = tal_strsplit(mr, take(vecs), "\n", STR_NO_EMPTY);
- plan_tests(343);
+ plan_tests(355);
for (size_t i = 0; lines[i]; i++) {
struct rune *rune1, *rune2;
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
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-HzAlPPw9mMV8xMQ==
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==
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);
}
struct tal_hdr {
struct list_node list;
- struct prop_hdr *prop;
+ /* Use is_prop_hdr tell if this is a struct prop_hdr or string! */
+ char *prop;
/* XOR with TAL_PTR_OBFUSTICATOR */
intptr_t parent_child;
size_t bytelen;
struct prop_hdr {
enum prop_type type;
- struct prop_hdr *next;
+ /* Use is_prop_hdr to tell if this is a struct prop_hdr or string! */
+ char *next;
};
struct children {
struct tal_hdr hdr;
struct children c;
} null_parent = { { { &null_parent.hdr.list, &null_parent.hdr.list },
- &null_parent.c.hdr, TAL_PTR_OBFUSTICATOR, 0 },
+ (char *)&null_parent.c.hdr, TAL_PTR_OBFUSTICATOR, 0 },
{ { CHILDREN, NULL },
&null_parent.hdr,
{ { &null_parent.c.children.n,
}
/* We carefully start all real properties with a zero byte. */
-static bool is_literal(const struct prop_hdr *prop)
+static struct prop_hdr *is_prop_hdr(const char *ptr)
{
- return ((char *)prop)[0] != 0;
+ if (*ptr != 0)
+ return NULL;
+ return (struct prop_hdr *)ptr;
}
#ifndef NDEBUG
check_bounds(ignore_destroying_bit(t->parent_child));
check_bounds(t->list.next);
check_bounds(t->list.prev);
- if (t->prop && !is_literal(t->prop))
- check_bounds(t->prop);
+ if (t->prop) {
+ struct prop_hdr *p = is_prop_hdr(t->prop);
+ if (p)
+ check_bounds(p);
+ }
return t;
}
enum tal_notify_type type, const void *info,
int saved_errno)
{
- const struct prop_hdr *p;
+ const char *ptr;
+ const struct prop_hdr *p;
- for (p = ctx->prop; p; p = p->next) {
+ for (ptr = ctx->prop; ptr && (p = is_prop_hdr(ptr)) != NULL; ptr = p->next) {
struct notifier *n;
- if (is_literal(p))
- break;
if (p->type != NOTIFIER)
continue;
n = (struct notifier *)p;
return ret;
}
-static struct prop_hdr **find_property_ptr(const struct tal_hdr *t,
- enum prop_type type)
+/* Returns a pointer to the pointer: can cast (*ret) to a (struct prop_ptr *) */
+static char **find_property_ptr(struct tal_hdr *t, enum prop_type type)
{
- struct prop_hdr **p;
+ char **ptr;
+ struct prop_hdr *p;
- for (p = (struct prop_hdr **)&t->prop; *p; p = &(*p)->next) {
- if (is_literal(*p)) {
- if (type == NAME)
- return p;
- break;
- }
- if ((*p)->type == type)
- return p;
- }
- return NULL;
+ /* NAME is special, as it can be a literal: see find_name_property */
+ assert(type != NAME);
+ for (ptr = &t->prop; *ptr; ptr = &p->next) {
+ if (!is_prop_hdr(*ptr))
+ break;
+ p = (struct prop_hdr *)*ptr;
+ if (p->type == type)
+ return ptr;
+ }
+ return NULL;
+}
+
+/* This is special:
+ * NULL - not found
+ * *literal: true - char **, pointer to literal pointer.
+ * *literal: false - struct prop_hdr **, pointer to header ptr.
+ */
+static char **find_name_property(struct tal_hdr *t, bool *literal)
+{
+ char **ptr;
+ struct prop_hdr *p;
+
+ for (ptr = &t->prop; *ptr; ptr = &p->next) {
+ if (!is_prop_hdr(*ptr)) {
+ *literal = true;
+ return ptr;
+ }
+ p = (struct prop_hdr *)*ptr;
+ if (p->type == NAME) {
+ *literal = false;
+ return ptr;
+ }
+ }
+ return NULL;
}
-static void *find_property(const struct tal_hdr *parent, enum prop_type type)
+static void *find_property(struct tal_hdr *parent, enum prop_type type)
{
- struct prop_hdr **p = find_property_ptr(parent, type);
+ char **ptr = find_property_ptr(parent, type);
- if (p)
- return *p;
+ if (ptr)
+ return (struct prop_hdr *)*ptr;
return NULL;
}
{
hdr->type = type;
hdr->next = parent->prop;
- parent->prop = hdr;
+ parent->prop = (char *)hdr;
}
static struct notifier *add_notifier_property(struct tal_hdr *t,
bool match_extra_arg,
void *extra_arg)
{
- struct prop_hdr **p;
+ char **ptr;
+ struct prop_hdr *p;
- for (p = (struct prop_hdr **)&t->prop; *p; p = &(*p)->next) {
+ for (ptr = &t->prop; *ptr; ptr = &p->next) {
struct notifier *n;
enum tal_notify_type types;
- if (is_literal(*p))
+ p = is_prop_hdr(*ptr);
+ if (!p)
break;
- if ((*p)->type != NOTIFIER)
+
+ if (p->type != NOTIFIER)
continue;
- n = (struct notifier *)*p;
+ n = (struct notifier *)p;
if (n->u.notifyfn != fn)
continue;
&& extra_arg != EXTRA_ARG(n))
continue;
- *p = (*p)->next;
- freefn(n);
+ *ptr = p->next;
+ freefn(p);
return types & ~(NOTIFY_IS_DESTRUCTOR|NOTIFY_EXTRA_ARG);
}
return 0;
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
{
- struct prop_hdr **prop, *p, *next;
+ struct prop_hdr *prop;
+ char *ptr, *next;
assert(!taken(from_tal_hdr(t)));
notify(t, TAL_NOTIFY_FREE, (tal_t *)orig, saved_errno);
/* Now free children and groups. */
- prop = find_property_ptr(t, CHILDREN);
+ prop = find_property(t, CHILDREN);
if (prop) {
struct tal_hdr *i;
- struct children *c = (struct children *)*prop;
+ struct children *c = (struct children *)prop;
while ((i = list_top(&c->children, struct tal_hdr, list))) {
list_del(&i->list);
}
/* Finally free our properties. */
- for (p = t->prop; p && !is_literal(p); p = next) {
- next = p->next;
- freefn(p);
+ for (ptr = t->prop; ptr && (prop = is_prop_hdr(ptr)); ptr = next) {
+ next = prop->next;
+ freefn(ptr);
}
freefn(t);
}
bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
{
struct tal_hdr *t = debug_tal(to_tal_hdr(ctx));
- struct prop_hdr **prop = find_property_ptr(t, NAME);
+ bool was_literal;
+ char **nptr;
/* Get rid of any old name */
- if (prop) {
- struct name *oldname = (struct name *)*prop;
- if (is_literal(&oldname->hdr))
- *prop = NULL;
- else {
- *prop = oldname->hdr.next;
+ nptr = find_name_property(t, &was_literal);
+ if (nptr) {
+ if (was_literal)
+ *nptr = NULL;
+ else {
+ struct name *oldname;
+
+ oldname = (struct name *)*nptr;
+ *nptr = oldname->hdr.next;
freefn(oldname);
- }
+ }
}
if (literal && name[0]) {
- struct prop_hdr **p;
+ char **ptr;
+ struct prop_hdr *prop;
/* Append literal. */
- for (p = &t->prop; *p && !is_literal(*p); p = &(*p)->next);
- *p = (struct prop_hdr *)name;
+ for (ptr = &t->prop; *ptr; ptr = &prop->next) {
+ prop = is_prop_hdr(*ptr);
+ if (!prop)
+ break;
+ }
+ *ptr = (char *)name;
} else if (!add_name_property(t, name))
return false;
const char *tal_name(const tal_t *t)
{
- struct name *n;
+ char **nptr;
+ bool literal;
- n = find_property(debug_tal(to_tal_hdr(t)), NAME);
- if (!n)
+ nptr = find_name_property(debug_tal(to_tal_hdr(t)), &literal);
+ if (!nptr)
return NULL;
+ if (literal)
+ return *nptr;
- if (is_literal(&n->hdr))
- return (const char *)n;
- return n->name;
+ return ((struct name *)(*nptr))->name;
}
size_t tal_bytelen(const tal_t *ptr)
}
ret = tal_alloc_arr_(ctx, size, n + extra, false, label);
- if (ret)
+ if (ret && p)
memcpy(ret, p, nbytes);
return ret;
}
static void dump_node(unsigned int indent, const struct tal_hdr *t)
{
unsigned int i;
- const struct prop_hdr *p;
+ const struct prop_hdr *prop;
+ const char *ptr;
for (i = 0; i < indent; i++)
fprintf(stderr, " ");
fprintf(stderr, "%p len=%zu", t, t->bytelen);
- for (p = t->prop; p; p = p->next) {
+ for (ptr = t->prop; ptr; ptr = prop->next) {
struct children *c;
struct name *n;
struct notifier *no;
- if (is_literal(p)) {
- fprintf(stderr, " \"%s\"", (const char *)p);
+ prop = is_prop_hdr(ptr);
+ if (!prop) {
+ fprintf(stderr, " \"%s\"", ptr);
break;
}
- switch (p->type) {
+ switch (prop->type) {
case CHILDREN:
- c = (struct children *)p;
+ c = (struct children *)prop;
fprintf(stderr, " CHILDREN(%p):parent=%p,children={%p,%p}",
- p, c->parent,
+ prop, c->parent,
c->children.n.prev, c->children.n.next);
break;
case NAME:
- n = (struct name *)p;
- fprintf(stderr, " NAME(%p):%s", p, n->name);
+ n = (struct name *)prop;
+ fprintf(stderr, " NAME(%p):%s", prop, n->name);
break;
case NOTIFIER:
- no = (struct notifier *)p;
- fprintf(stderr, " NOTIFIER(%p):fn=%p", p, no->u.notifyfn);
+ no = (struct notifier *)prop;
+ fprintf(stderr, " NOTIFIER(%p):fn=%p", prop, no->u.notifyfn);
break;
default:
- fprintf(stderr, " **UNKNOWN(%p):%i**", p, p->type);
+ fprintf(stderr, " **UNKNOWN(%p):%i**", prop, prop->type);
}
}
fprintf(stderr, "\n");
dump_node(level, t);
- children = find_property(t, CHILDREN);
+ children = find_property((struct tal_hdr *)t, CHILDREN);
if (children) {
struct tal_hdr *i;
static bool check_node(struct children *parent_child,
struct tal_hdr *t, const char *errorstr)
{
- struct prop_hdr *p;
+ struct prop_hdr *prop;
+ char *p;
struct name *name = NULL;
struct children *children = NULL;
if (ignore_destroying_bit(t->parent_child) != parent_child)
return check_err(t, errorstr, "incorrect parent");
- for (p = t->prop; p; p = p->next) {
- if (is_literal(p)) {
+ for (p = t->prop; p; p = prop->next) {
+ prop = is_prop_hdr(p);
+ if (!prop) {
if (name)
return check_err(t, errorstr,
"has extra literal");
break;
}
- if (!in_bounds(p))
+ if (!in_bounds(prop))
return check_err(t, errorstr,
"has bad property pointer");
- switch (p->type) {
+ switch (prop->type) {
case CHILDREN:
if (children)
return check_err(t, errorstr,
"has two child nodes");
- children = (struct children *)p;
+ children = (struct children *)prop;
break;
case NOTIFIER:
break;
if (name)
return check_err(t, errorstr,
"has two names");
- name = (struct name *)p;
+ name = (struct name *)prop;
break;
default:
return check_err(t, errorstr, "has unknown property");
void *new = realloc(old, size);
if (new == old) {
void *p = malloc(size);
- memcpy(p, old, size);
- free(old);
+ memcpy(p, new, size);
+ free(new);
new = p;
}
return new;
return 0;
}
assert(step_bits < SIZET_BITS);
- return (size_t)(val - min) >> step_bits;
+ return ((size_t)val - (size_t)min) >> step_bits;
}
/* Return the min value in bucket b. */
return min;
}
assert(step_bits < SIZET_BITS);
- return min + ((ssize_t)b << step_bits);
+ return min + ((size_t)b << step_bits);
}
/* Does shifting by this many bits truncate the number? */
if (bits == 0) {
return false;
}
+ if (bits >= SIZET_BITS) {
+ return true;
+ }
return ((num << bits) >> 1) != (num << (bits - 1));
}
/* If we don't have sufficient range, increase step bits until
* buckets cover entire range of ssize_t anyway. */
- range = (new_max - new_min) + 1;
+ range = ((size_t)new_max - (size_t)new_min) + 1;
while (!shift_overflows(tally->buckets, tally->step_bits)
&& range > ((size_t)tally->buckets << tally->step_bits)) {
/* Collapse down. */
memset(tally->counts, 0, sizeof(tally->counts[0]) * old_min);
/* If we moved boundaries, adjust buckets to that ratio. */
- spill = (tally->min - new_min) % (1 << tally->step_bits);
- for (i = 0; i < tally->buckets-1; i++) {
- size_t adjust = (tally->counts[i] >> tally->step_bits) * spill;
- tally->counts[i] -= adjust;
- tally->counts[i+1] += adjust;
+ if (tally->step_bits < SIZET_BITS) {
+ spill = (tally->min - new_min) % ((size_t)1 << tally->step_bits);
+ for (i = 0; i < tally->buckets-1; i++) {
+ size_t adjust = (tally->counts[i] >> tally->step_bits) * spill;
+ tally->counts[i] -= adjust;
+ tally->counts[i+1] += adjust;
+ }
}
update:
* It evaluates to @x so you can chain it.
*/
#define tcon_check_ptr(x, canary, expr) \
- (sizeof(&(x)->_tcon[0].canary == (expr)) ? (x) : (x))
-
+ (sizeof((expr) ? (expr) : &(x)->_tcon[0].canary) ? (x) : (x))
/**
* tcon_type - the type within a container (or void *)
int main(void)
{
struct info_tcon info;
- struct outer ovar;
+ struct outer ovar = { 0, { 0 } };
#ifdef FAIL
#if !HAVE_TYPEOF
#error We cannot detect type problems without HAVE_TYPEOF
{
TCON_WRAP(struct info_base,
TCON_CONTAINER(concan, struct outer, inner)) info;
- struct outer ovar;
+ struct outer ovar = { 0, { 0 } };
#ifdef FAIL
#if !HAVE_TYPEOF
#error We cannot detect type problems without HAVE_TYPEOF
int main(void)
{
struct info_tcon info;
- struct outer ovar;
+ struct outer ovar = { 0, { 0 } };
#ifdef FAIL
#if !HAVE_TYPEOF
#error We cannot detect type problems without HAVE_TYPEOF
{
TCON_WRAP(struct info_base,
TCON_CONTAINER(concan, struct outer, inner)) info;
- struct outer ovar;
+ struct outer ovar = { 0, { 0 } };
#ifdef FAIL
#if !HAVE_TYPEOF
#error We cannot detect type problems without HAVE_TYPEOF
--- /dev/null
+../../licenses/BSD-MIT
\ No newline at end of file
--- /dev/null
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * ungraph - extract a graph from an ASCII diagram.
+ *
+ * This code takes an ASCII diagram and converts it to a graph.
+ * The following things are assumed:
+ * 1. The input consists of \n-terminated lines
+ * 2. /-\|+ are used for edges.
+ * 3. <^>v are used for arrowheads.
+ * 4. + can be used to cross-over.
+ * 5. No arrowheads or both-ended arrowheads are shortcuts for "both ways".
+ * 6. Edges can turn with or without a +, by up to 90 degrees.
+ * 7. Edges must go from one node name to another.
+ * 8. Any other text is an edge label which must be next to an edge or
+ * another label.
+ *
+ * License: BSD-MIT
+ * Example:
+ * // Convert an ASCII graph to Graphviz dot format
+ * #include <ccan/err/err.h>
+ * #include <ccan/grab_file/grab_file.h>
+ * #include <ccan/ungraph/ungraph.h>
+ *
+ * // Just return the name as our node.
+ * static void *add_node(const tal_t *ctx,
+ * const char *name,
+ * const char **errstr,
+ * void *unused)
+ * {
+ * return (void *)name;
+ * }
+ *
+ * static const char *add_edge(const tal_t *ctx,
+ * void *source_node,
+ * void *dest_node,
+ * bool bidir,
+ * const char **labels,
+ * void *arg)
+ * {
+ * printf("%s -> %s;\n",
+ * (char *)source_node, (char *)dest_node);
+ * if (bidir)
+ * printf("%s -> %s;\n",
+ * (char *)dest_node, (char *)source_node);
+ * return NULL;
+ * }
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * const char *graph = grab_file(NULL, argv[1], NULL), *errmsg;
+ * printf("digraph %s {\n", argv[1] ? argv[1] : "stdin");
+ * errmsg = ungraph(NULL, graph, add_node, add_edge, NULL);
+ * if (errmsg)
+ * errx(1, "%s", errmsg);
+ * printf("}");
+ * }
+ *
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/tal\n");
+ printf("ccan/tal/str\n");
+ printf("ccan/typesafe_cb\n");
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+#include <ccan/ungraph/ungraph.h>
+/* Include the C files directly. */
+#include <ccan/ungraph/ungraph.c>
+#include <ccan/tap/tap.h>
+#include <assert.h>
+
+static void *add_node(const tal_t *ctx,
+ const char *name,
+ const char **errstr,
+ char **out)
+{
+ tal_append_fmt(out, "add_node %s\n", (char *)name);
+ return (void *)tal_steal(ctx, name);
+}
+
+static const char *add_edge(const tal_t *ctx,
+ void *source_node,
+ void *dest_node,
+ bool bidir,
+ const char **labels,
+ char **out)
+{
+ tal_append_fmt(out, "add_edge %s-%s bidir=%i\n",
+ (char *)source_node,
+ (char *)dest_node,
+ bidir);
+ for (size_t i = 0; i < tal_count(labels); i++)
+ tal_append_fmt(out, "- label %s\n", labels[i]);
+ return NULL;
+}
+
+int main(void)
+{
+ const tal_t *ctx = tal(NULL, char);
+ char *out = tal_arrz(ctx, char, 1);
+ /* This is how many tests you plan to run */
+ plan_tests(16);
+
+ ok1(ungraph(ctx,
+ "AAA----->BBB",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_edge AAA-BBB bidir=0\n") == 0);
+
+ out = tal_arrz(ctx, char, 1);
+ ok1(ungraph(ctx,
+ "AAA<------BBB",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_edge BBB-AAA bidir=0\n") == 0);
+
+ out = tal_arrz(ctx, char, 1);
+ ok1(ungraph(ctx,
+ "AAA------BBB",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_edge AAA-BBB bidir=1\n") == 0);
+
+ out = tal_arrz(ctx, char, 1);
+ ok1(ungraph(ctx,
+ "AAA<------>BBB",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_edge AAA-BBB bidir=1\n") == 0);
+
+ out = tal_arrz(ctx, char, 1);
+ ok1(ungraph(ctx,
+ "AAA\n"
+ " ^ \n"
+ " | \n"
+ " | \n"
+ " v \n"
+ "BBB",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_edge AAA-BBB bidir=1\n") == 0);
+
+ out = tal_arrz(ctx, char, 1);
+ ok1(ungraph(ctx,
+ "AAA\n"
+ " / \n"
+ "| \n"
+ " \\ \n"
+ " v \n"
+ " BBB\n",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_edge AAA-BBB bidir=0\n") == 0);
+
+ out = tal_arrz(ctx, char, 1);
+ ok1(ungraph(ctx,
+ "AAA\n"
+ " / \n"
+ "|xyx \n"
+ " \\ \n"
+ " v \n"
+ " BBB",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_edge AAA-BBB bidir=0\n"
+ "- label xyx\n") == 0);
+
+ out = tal_arrz(ctx, char, 1);
+ ok1(ungraph(ctx,
+ " AAA \n"
+ " | \n"
+ "A-+----B \n"
+ " | LABEL \n"
+ " | xyz\n"
+ " v \n"
+ " BBB",
+ add_node, add_edge, &out) == NULL);
+ ok1(strcmp(out,
+ "add_node AAA\n"
+ "add_node BBB\n"
+ "add_node A\n"
+ "add_node B\n"
+ "add_edge AAA-BBB bidir=0\n"
+ "add_edge A-B bidir=1\n"
+ "- label LABEL\n"
+ "- label xyz\n") == 0);
+
+ tal_free(ctx);
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
--- /dev/null
+/* MIT (BSD) license - see LICENSE file for details */
+#include <ccan/ungraph/ungraph.h>
+#include <ccan/tal/str/str.h>
+
+struct xy {
+ size_t x, y;
+};
+
+struct text {
+ struct xy xy;
+ size_t width;
+ const char *text;
+ /* If it's a node, this is non-NULL */
+ void *node;
+ /* NULL if none found, edge it one found, self if > 1 */
+ struct edge *nearest_edge;
+};
+
+struct edge {
+ struct text *src, *dst;
+ bool bidir;
+ const char **labels;
+};
+
+/* This means we actually found two "nearest_edge" */
+static struct edge fake_edge;
+
+#define EDGES "+/-\\|"
+#define ARROWS "<^>v"
+
+enum dir {
+ UP,
+ UP_RIGHT,
+ RIGHT,
+ DOWN_RIGHT,
+ DOWN,
+ DOWN_LEFT,
+ LEFT,
+ UP_LEFT,
+ INVALID,
+};
+
+static enum dir opposite_dir(enum dir dir)
+{
+ return (dir + 4) % 8;
+}
+
+static enum dir clockwise(enum dir dir)
+{
+ return (dir + 1) % 8;
+}
+
+static enum dir anticlockwise(enum dir dir)
+{
+ return (dir + 7) % 8;
+}
+
+static enum dir dir_away(const struct text *t, struct xy xy)
+{
+ int xdir, ydir;
+ enum dir dirs[3][3] = {{UP_LEFT, UP, UP_RIGHT},
+ {LEFT, INVALID, RIGHT},
+ {DOWN_LEFT, DOWN, DOWN_RIGHT}};
+
+ if (xy.y < t->xy.y)
+ ydir = -1;
+ else if (xy.y > t->xy.y)
+ ydir = 1;
+ else
+ ydir = 0;
+ if (xy.x >= t->xy.x + t->width)
+ xdir = 1;
+ else if (xy.x < t->xy.x)
+ xdir = -1;
+ else
+ xdir = 0;
+
+ return dirs[ydir+1][xdir+1];
+}
+
+static char line_for_dir(enum dir dir)
+{
+ switch (dir) {
+ case UP:
+ case DOWN:
+ return '|';
+ case UP_RIGHT:
+ case DOWN_LEFT:
+ return '/';
+ case RIGHT:
+ case LEFT:
+ return '-';
+ case DOWN_RIGHT:
+ case UP_LEFT:
+ return '\\';
+ case INVALID:
+ break;
+ }
+ abort();
+}
+
+static char arrow_for_dir(enum dir dir)
+{
+ switch (dir) {
+ case UP:
+ case UP_RIGHT:
+ case UP_LEFT:
+ return '^';
+ case DOWN:
+ case DOWN_RIGHT:
+ case DOWN_LEFT:
+ return 'v';
+ case LEFT:
+ return '<';
+ case RIGHT:
+ return '>';
+ case INVALID:
+ break;
+ }
+ abort();
+}
+
+static struct xy move_in_dir(struct xy xy, enum dir dir)
+{
+ switch (dir) {
+ case UP:
+ xy.y = xy.y - 1;
+ return xy;
+ case DOWN:
+ xy.y = xy.y + 1;
+ return xy;
+ case UP_RIGHT:
+ xy.x = xy.x + 1;
+ xy.y = xy.y - 1;
+ return xy;
+ case DOWN_LEFT:
+ xy.x = xy.x - 1;
+ xy.y = xy.y + 1;
+ return xy;
+ case RIGHT:
+ xy.x = xy.x + 1;
+ return xy;
+ case LEFT:
+ xy.x = xy.x - 1;
+ return xy;
+ case DOWN_RIGHT:
+ xy.x = xy.x + 1;
+ xy.y = xy.y + 1;
+ return xy;
+ case UP_LEFT:
+ xy.x = xy.x - 1;
+ xy.y = xy.y - 1;
+ return xy;
+ case INVALID:
+ break;
+ }
+ abort();
+}
+
+static char *sqc(char **sq, struct xy xy)
+{
+ return &sq[xy.y][xy.x];
+}
+
+/* Try straight ahead first, then a bit to either side, then
+ * finally left and right */
+static struct xy scan_move_next(struct xy xy, enum dir start, enum dir *cur)
+{
+ if (*cur == start)
+ *cur = clockwise(start);
+ else if (*cur == clockwise(start))
+ *cur = anticlockwise(start);
+ else if (*cur == anticlockwise(start))
+ *cur = anticlockwise(anticlockwise(start));
+ else if (*cur == anticlockwise(anticlockwise(start)))
+ *cur = clockwise(clockwise(start));
+ else {
+ *cur = INVALID;
+ return xy;
+ }
+ return move_in_dir(xy, *cur);
+}
+
+static void start_perimeter(struct xy *xyp, enum dir *dirp, struct xy xy)
+{
+ *dirp = RIGHT;
+ xyp->x = xy.x - 1;
+ xyp->y = xy.y - 1;
+}
+
+static void next_perimeter(struct xy *xyp, enum dir *dirp, struct xy xy, size_t width)
+{
+ *xyp = move_in_dir(*xyp, *dirp);
+ if (*dirp == RIGHT && xyp->x == xy.x + width)
+ *dirp = DOWN;
+ else if (*dirp == DOWN && xyp->y == xy.y + 1)
+ *dirp = LEFT;
+ else if (*dirp == LEFT && xyp->x == xy.x - 1)
+ *dirp = UP;
+ else if (*dirp == UP && xyp->y == xy.y - 2)
+ *dirp = INVALID;
+}
+
+/* Useful iterators. */
+#define for_each_scan_dir(xyp, dirp, xy, dir) \
+ for (*dirp = dir, *xyp = move_in_dir(xy, *dirp); \
+ *dirp != INVALID; \
+ *xyp = scan_move_next(xy, dir, dirp))
+
+#define for_each_perimeter(xyp, dirp, xy, width) \
+ for (start_perimeter(xyp, dirp, xy); \
+ *dirp != INVALID; \
+ next_perimeter(xyp, dirp, xy, width))
+
+/* Canonicalizes str into array of characters, finds text strings. */
+static char **square(const tal_t *ctx,
+ const char *str,
+ struct text **texts)
+{
+ size_t width = 0, height = 0;
+ size_t line_len = 0;
+ char **sq;
+ struct text *cur_text;
+ size_t strlen;
+
+ *texts = tal_arr(ctx, struct text, 0);
+
+ strlen = 0;
+ for (size_t i = 0; str[i]; i++) {
+ if (str[i] == '\n') {
+ height++;
+ line_len = 0;
+ } else {
+ line_len++;
+ if (line_len > width)
+ width = line_len;
+ }
+ strlen++;
+ }
+
+ /* If didn't end in \n, it's implied */
+ if (line_len != 0) {
+ height++;
+ strlen++;
+ }
+
+ /* For analysis simplicity, create a blank border. */
+ sq = tal_arr(ctx, char *, height + 2);
+ for (size_t i = 0; i < height + 2; i++) {
+ sq[i] = tal_arr(sq, char, width + 2);
+ memset(sq[i], ' ', width + 2);
+ }
+
+ /* Copy across and find text */
+ cur_text = NULL;
+ width = height = 1;
+ for (size_t i = 0; i < strlen; i++) {
+ bool end_text;
+ bool eol;
+
+ eol = (str[i] == '\n' || str[i] == '\0');
+ if (!eol)
+ sq[height][width] = str[i];
+
+ /* v by itself handled separately below */
+ if (strchr(EDGES ARROWS "\n", str[i]) && str[i] != 'v') {
+ end_text = (cur_text != NULL);
+ } else if (cur_text) {
+ /* Two spaces ends text */
+ end_text = (str[i] == ' ' && str[i-1] == ' ') || eol;
+ } else if (str[i] != ' ') {
+ size_t num_texts = tal_count(*texts);
+ tal_resize(texts, num_texts+1);
+ cur_text = &(*texts)[num_texts];
+ cur_text->xy.x = width;
+ cur_text->xy.y = height;
+ cur_text->width = 0;
+ cur_text->node = NULL;
+ cur_text->nearest_edge = NULL;
+ end_text = false;
+ } else
+ end_text = false;
+
+ if (end_text) {
+ /* Trim final space */
+ if (sq[cur_text->xy.y][cur_text->xy.x + cur_text->width-1] == ' ')
+ cur_text->width--;
+ /* Ignore lone 'v' */
+ if (cur_text->width == 1 && sq[cur_text->xy.y][cur_text->xy.x] == 'v')
+ tal_resize(texts, tal_count(*texts)-1);
+ else {
+ cur_text->text = tal_strndup(ctx, &sq[cur_text->xy.y][cur_text->xy.x],
+ cur_text->width);
+ }
+ cur_text = NULL;
+ }
+
+ if (cur_text)
+ cur_text->width++;
+ if (eol) {
+ height++;
+ width = 1;
+ } else
+ width++;
+ }
+
+ return sq;
+}
+
+/* If text was not previously a node, it is now! */
+static const char *text_now_node(const tal_t *ctx,
+ char **sq,
+ struct text *text,
+ void *(*add_node)(const tal_t *ctx,
+ const char *name,
+ const char **errstr,
+ void *arg),
+ void *arg)
+{
+ const char *err;
+
+ /* Already a node? */
+ if (text->node)
+ return NULL;
+
+ text->node = add_node(ctx, text->text, &err, arg);
+ if (!text->node)
+ return err;
+ return NULL;
+}
+
+static bool correct_line_char(char c, enum dir dir, enum dir *newdir)
+{
+ if (c == line_for_dir(dir)) {
+ *newdir = dir;
+ return true;
+ } else if (c == line_for_dir(anticlockwise(dir))) {
+ *newdir = anticlockwise(dir);
+ return true;
+ } else if (c == line_for_dir(clockwise(dir))) {
+ *newdir = clockwise(dir);
+ return true;
+ }
+ return false;
+}
+
+static bool seek_line(char **sq, struct xy *xy, enum dir *dir)
+{
+ struct xy scan;
+ enum dir scandir;
+
+ for_each_scan_dir(&scan, &scandir, *xy, *dir) {
+ if (correct_line_char(*sqc(sq, scan), scandir, &scandir))
+ goto found;
+ /* + in front always works */
+ if (*dir == scandir && *sqc(sq, scan) == '+')
+ goto found;
+ }
+ return false;
+
+found:
+ *xy = scan;
+ *dir = scandir;
+ return true;
+}
+
+static bool seek_arrowhead(char **sq, struct xy *xy, enum dir *dir)
+{
+ struct xy scan;
+ enum dir scandir;
+
+ for_each_scan_dir(&scan, &scandir, *xy, *dir) {
+ if (strchr(ARROWS, *sqc(sq, scan))) {
+ *xy = scan;
+ *dir = scandir;
+ return true;
+ }
+ }
+ return false;
+}
+
+static struct text *in_text(struct text *texts, struct xy xy)
+{
+ for (size_t i = 0; i < tal_count(texts); i++) {
+ if (texts[i].xy.y != xy.y)
+ continue;
+ if (xy.x >= texts[i].xy.x + texts[i].width)
+ continue;
+ if (xy.x < texts[i].xy.x)
+ continue;
+ return texts + i;
+ }
+ return NULL;
+}
+
+static struct text *seek_text(struct text *texts,
+ struct xy xy, enum dir dir)
+{
+ struct xy scan;
+ enum dir scandir;
+
+ for_each_scan_dir(&scan, &scandir, xy, dir) {
+ struct text *t = in_text(texts, scan);
+ if (t)
+ return t;
+ }
+ return NULL;
+}
+
+static void erase_line(char **sq,
+ struct xy xy,
+ enum dir dir,
+ enum dir prev_dir)
+{
+ char c = ' ';
+
+ /* If we go straight through a +, convert for crossover */
+ if (prev_dir == dir && *sqc(sq, xy) == '+') {
+ if (dir == UP || dir == DOWN)
+ c = '-';
+ if (dir == LEFT || dir == RIGHT)
+ c = '|';
+ }
+ *sqc(sq, xy) = c;
+}
+
+static bool in_nearby(struct text **nearby, const struct text *t)
+{
+ for (size_t i = 0; i < tal_count(nearby); i++) {
+ if (nearby[i] == t)
+ return true;
+ }
+ return false;
+}
+
+static void add_nearby(struct text ***nearby,
+ struct text *texts,
+ struct xy xy)
+{
+ struct xy perim;
+ enum dir pdir;
+ size_t n = tal_count(*nearby);
+
+ for_each_perimeter(&perim, &pdir, xy, 1) {
+ struct text *t = in_text(texts, perim);
+ if (!t)
+ continue;
+ /* Don't care if it's already a node */
+ if (t->node)
+ continue;
+ if (in_nearby(*nearby, t))
+ continue;
+ tal_resize(nearby, n+1);
+ (*nearby)[n++] = t;
+ }
+}
+
+/* Clears lines as it goes. */
+static struct text *follow_line(char **sq,
+ struct text *texts,
+ struct xy xy,
+ enum dir dir,
+ bool *arrow_src,
+ bool *arrow_dst,
+ bool *dangling,
+ struct text ***nearby)
+{
+ char expect_arrow = arrow_for_dir(opposite_dir(dir));
+ enum dir prev_dir;
+
+ *nearby = tal_arr(sq, struct text *, 0);
+
+ if (*sqc(sq, xy) == expect_arrow) {
+ *arrow_src = true;
+ } else if (*sqc(sq, xy) == line_for_dir(dir)) {
+ *arrow_src = false;
+ } else if (*sqc(sq, xy) == line_for_dir(anticlockwise(dir))) {
+ *arrow_src = false;
+ dir = anticlockwise(dir);
+ } else if (*sqc(sq, xy) == line_for_dir(clockwise(dir))) {
+ *arrow_src = false;
+ dir = clockwise(dir);
+ } else {
+ *dangling = false;
+ /* No arrow is fine. */
+ return NULL;
+ }
+
+ erase_line(sq, xy, dir, INVALID);
+ add_nearby(nearby, texts, xy);
+
+ *arrow_dst = false;
+ prev_dir = dir;
+ for (;;) {
+ /* Try to continue line */
+ if (!*arrow_dst && seek_line(sq, &xy, &dir)) {
+ erase_line(sq, xy, dir, prev_dir);
+ add_nearby(nearby, texts, xy);
+ prev_dir = dir;
+ continue;
+ }
+ /* Look for arrow */
+ if (!*arrow_dst && seek_arrowhead(sq, &xy, &dir)) {
+ erase_line(sq, xy, dir, prev_dir);
+ add_nearby(nearby, texts, xy);
+ *arrow_dst = true;
+ prev_dir = dir;
+ continue;
+ }
+ break;
+ }
+
+ /* Must be in text! */
+ *dangling = true;
+ return seek_text(texts, xy, dir);
+}
+
+static const char *try_create_edge(const tal_t *ctx,
+ char **sq,
+ struct text *texts,
+ struct xy xy,
+ struct text *src,
+ void *(*add_node)(const tal_t *ctx,
+ const char *name,
+ const char **errstr,
+ void *arg),
+ void *arg,
+ struct edge **edge)
+{
+ struct text *dst;
+ bool arrow_src, arrow_dst, dangling;
+ struct text **nearby;
+ const char *err;
+
+ *edge = NULL;
+ if (in_text(texts, xy))
+ return NULL;
+
+ dst = follow_line(sq, texts, xy, dir_away(src, xy), &arrow_src, &arrow_dst, &dangling, &nearby);
+ if (!dst) {
+ if (dangling)
+ return tal_fmt(ctx, "Found dangling arrow at (%zu,%zu)", xy.x-1, xy.y-1);
+ return NULL;
+ }
+
+ /* If you weren't a node before, you are now! */
+ err = text_now_node(ctx, sq, src, add_node, arg);
+ if (err)
+ return err;
+ err = text_now_node(ctx, sq, dst, add_node, arg);
+ if (err)
+ return err;
+
+ /* No arrows equiv to both arrows */
+ if (!arrow_src && !arrow_dst)
+ arrow_src = arrow_dst = true;
+
+ *edge = tal(NULL, struct edge);
+ if (arrow_dst) {
+ (*edge)->src = src;
+ (*edge)->dst = dst;
+ (*edge)->bidir = arrow_src;
+ } else {
+ (*edge)->src = dst;
+ (*edge)->dst = src;
+ (*edge)->bidir = false;
+ }
+ (*edge)->labels = tal_arr(*edge, const char *, 0);
+
+ /* Now record any texts it passed by, in case they're labels */
+ for (size_t i = 0; i < tal_count(nearby); i++) {
+ /* We might have just made it a node */
+ if (nearby[i]->node)
+ continue;
+ /* Already has an edge? Mark it as near two, to error
+ * later if it's a label */
+ if (nearby[i]->nearest_edge)
+ nearby[i]->nearest_edge = &fake_edge;
+ else
+ nearby[i]->nearest_edge = *edge;
+ }
+
+ return NULL;
+}
+
+static const char *scan_for_unused(const tal_t *ctx,
+ struct text *texts,
+ char **sq)
+{
+ struct xy xy;
+ for (xy.y = 0; xy.y < tal_count(sq); xy.y++) {
+ for (xy.x = 0; xy.x < tal_count(sq[xy.y]); xy.x++) {
+ if (in_text(texts, xy))
+ continue;
+ if (*sqc(sq,xy) != ' ')
+ return tal_fmt(ctx, "Unused '%c' at (%zu,%zu)",
+ *sqc(sq, xy), xy.x-1, xy.y-1);
+ }
+ }
+ return NULL;
+}
+
+static void add_label(struct edge *edge, const struct text *label)
+{
+ size_t n = tal_count(edge->labels);
+ tal_resize(&edge->labels, n+1);
+ edge->labels[n] = label->text;
+}
+
+const char *ungraph_(const tal_t *ctx,
+ const char *str,
+ void *(*add_node)(const tal_t *ctx,
+ const char *name,
+ const char **errstr,
+ void *arg),
+ const char *(*add_edge)(const tal_t *ctx,
+ void *source_node,
+ void *dest_node,
+ bool bidir,
+ const char **labels,
+ void *arg),
+ void *arg)
+{
+ /* To hold all our temporaries! */
+ const tal_t *sub = tal(ctx, char);
+ char **sq;
+ struct text *texts, *remaining_label;
+ const char *err;
+ bool progress;
+ struct edge **edges = tal_arr(sub, struct edge *, 0);
+ size_t num_edges = 0;
+
+ /* We create canonical square, find texts. */
+ sq = square(sub, str, &texts);
+
+ /* Now search for arrows around each text, cleaning
+ * as we go! */
+ for (size_t i = 0; i < tal_count(texts); i++) {
+ struct xy perim;
+ enum dir pdir;
+ struct text *t = &texts[i];
+
+ for_each_perimeter(&perim, &pdir, t->xy, t->width) {
+ struct edge *edge;
+ err = try_create_edge(ctx, sq, texts, perim, t, add_node, arg, &edge);
+ if (err)
+ goto fail;
+ if (edge) {
+ tal_resize(&edges, num_edges+1);
+ edges[num_edges++] = tal_steal(edges, edge);
+ }
+ }
+ }
+
+ /* Now attach any remaining labels */
+ for (size_t i = 0; i < tal_count(texts); i++) {
+ struct text *t = &texts[i];
+
+ if (t->node)
+ continue;
+ if (t->nearest_edge == &fake_edge) {
+ err = tal_fmt(ctx, "Label at (%zu,%zu) near more than one edge",
+ t->xy.x-1, t->xy.y-1);
+ goto fail;
+ }
+ if (t->nearest_edge)
+ add_label(t->nearest_edge, t);
+ }
+
+ /* Any remaining labels must be attached to already-attached labels */
+ do {
+ progress = false;
+ remaining_label = NULL;
+
+ for (size_t i = 0; i < tal_count(texts); i++) {
+ struct xy perim;
+ enum dir pdir;
+ struct text *t = &texts[i];
+
+ if (t->node || t->nearest_edge)
+ continue;
+
+ remaining_label = t;
+ for_each_perimeter(&perim, &pdir, t->xy, t->width) {
+ struct text *neighbor = in_text(texts, perim);
+ if (!neighbor || neighbor->node || !neighbor->nearest_edge)
+ continue;
+ t->nearest_edge = neighbor->nearest_edge;
+ add_label(t->nearest_edge, t);
+ progress = true;
+ break;
+ }
+ }
+ } while (progress);
+
+ if (remaining_label) {
+ err = tal_fmt(ctx, "Label at (%zu,%zu) not near any edge",
+ remaining_label->xy.x-1,
+ remaining_label->xy.y-1);
+ goto fail;
+ }
+
+ err = scan_for_unused(ctx, texts, sq);
+ if (err)
+ goto fail;
+
+ /* Now add edges, complete with labels */
+ for (size_t i = 0; i < tal_count(edges); i++) {
+ err = add_edge(ctx, edges[i]->src->node, edges[i]->dst->node,
+ edges[i]->bidir, edges[i]->labels, arg);
+ if (err)
+ goto fail;
+ }
+
+ tal_free(sub);
+ return NULL;
+
+fail:
+ tal_free(sub);
+ return err;
+}
--- /dev/null
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_UNGRAPH_H
+#define CCAN_UNGRAPH_H
+#include <ccan/tal/tal.h>
+#include <ccan/typesafe_cb/typesafe_cb.h>
+
+/**
+ * ungraph: extract a graph from an ASCII graph.
+ * @ctx: context for callbacks, and/or returned errstr.
+ * @str: a string containing a graph.
+ * @add_node: callback for a new node, returns node.
+ * @add_edge: callback for a new edge, with tal_count(labels).
+ * @arg: callback argument.
+ *
+ * On success, returns NULL. On failure, returns some error message
+ * (allocated off @ctx, or returned from callbacks).
+ *
+ * If @add_node returns NULL, it must set @errstr. @add_edge
+ * returns the error message directly.
+ *
+ * @add_node and @add_edge can tal_steal the name/labels if they want,
+ * otherwise they will be freed.
+ */
+const char *ungraph_(const tal_t *ctx,
+ const char *str,
+ void *(*add_node)(const tal_t *ctx,
+ const char *name,
+ const char **errstr,
+ void *arg),
+ const char *(*add_edge)(const tal_t *ctx,
+ void *source_node,
+ void *dest_node,
+ bool bidir,
+ const char **labels,
+ void *arg),
+ void *arg);
+
+#define ungraph(ctx, str, add_node, add_edge, arg) \
+ ungraph_((ctx), (str), \
+ typesafe_cb_preargs(void *, void *, \
+ (add_node), (arg), \
+ const tal_t *, \
+ const char *, \
+ const char **errstr), \
+ typesafe_cb_preargs(const char *, void *, \
+ (add_edge), (arg), \
+ const tal_t *, \
+ void *, \
+ void *, \
+ bool, \
+ const char **), \
+ arg)
+#endif /* CCAN_UNGRAPH_H */
*/
static inline struct version version(uint16_t major, uint16_t minor)
{
- struct version v = { ._v = major << 16 | minor };
+ struct version v = { ._v = (uint32_t)major << 16 | minor };
return v;
}
int old_len, len;
/* This length includes nul terminator! */
old_len = tal_count(c->output);
- tal_resize(&c->output, old_len + 1024);
- len = read(c->output_fd, c->output + old_len - 1, 1024);
+ tal_resize(&c->output, old_len + 65536);
+ len = read(c->output_fd, c->output + old_len - 1, 65536);
if (len < 0)
err(1, "Reading from async command");
- tal_resize(&c->output, old_len + len);
+ if (len != 65536)
+ tal_resize(&c->output, old_len + len);
c->output[old_len + len - 1] = '\0';
if (len == 0) {
struct rusage ru;
"return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
{ "HAVE_BUILTIN_CTZ", "__builtin_ctz support",
"INSIDE_MAIN", NULL, NULL,
- "return __builtin_ctz(1 << (sizeof(int)*8 - 1)) == (sizeof(int)*8 - 1) ? 0 : 1;" },
+ "return __builtin_ctz(1U << (sizeof(int)*8 - 1)) == (sizeof(int)*8 - 1) ? 0 : 1;" },
{ "HAVE_BUILTIN_CTZL", "__builtin_ctzl support",
"INSIDE_MAIN", NULL, NULL,
"return __builtin_ctzl(1UL << (sizeof(long)*8 - 1)) == (sizeof(long)*8 - 1) ? 0 : 1;" },
"#include <stddef.h>\n"
"#include <ucontext.h>\n"
"static int worked = 0;\n"
- "static char stack[1024];\n"
+ "static char stack[8192];\n"
"static ucontext_t a, b;\n"
"static void fn(void *p, void *q) {\n"
" void *cp = &worked;\n"
char *config_header;
config_header = grab_file(NULL, fname);
- tal_free(fname);
- if (!config_header)
+ if (!config_header) {
+ tal_free(fname);
return NULL;
+ }
lines = tal_strsplit(config_header, config_header, "\n", STR_EMPTY_OK);
for (i = 0; i < tal_count(lines) - 1; i++) {
fname, cflags);
}
}
+ tal_free(fname);
return config_header;
}