From d87a25dc5510f47ed876985f6014dbfa4a96f1aa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Jan 2023 12:49:22 +1030 Subject: [PATCH 01/16] rbuf: avoid doing memchr(NULL, ..., 0). Signed-off-by: Rusty Russell --- ccan/rbuf/rbuf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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) -- 2.39.2 From fd8e571f5dfa09db184ed6e5557ac735278aefbd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Jan 2023 12:50:22 +1030 Subject: [PATCH 02/16] objset: avoid memleak report in example. Signed-off-by: Rusty Russell --- ccan/objset/_info | 2 ++ 1 file changed, 2 insertions(+) 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 -. -- 2.39.2 From 08bf6bb9e7ad255a592e7a50897ca73ad800582c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Jan 2023 12:51:22 +1030 Subject: [PATCH 03/16] tally: avoid complaints about bitshifts on giant bucket sizes. Signed-off-by: Rusty Russell --- ccan/tally/tally.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) 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: -- 2.39.2 From 1ee3028a58e7c4a78491dd07a97feaecbdf5731f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Jan 2023 12:52:22 +1030 Subject: [PATCH 04/16] stringbuilder: avoid overwriting end of buffer with nul terminator. Signed-off-by: Rusty Russell --- ccan/stringbuilder/stringbuilder.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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); } -- 2.39.2 From 3942778bb3f61cb580dd088c8449ca6dfdf6b7bc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Jan 2023 12:53:22 +1030 Subject: [PATCH 05/16] fdpass: avoid memory leak in example. Signed-off-by: Rusty Russell --- ccan/io/fdpass/_info | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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. -- 2.39.2 From 5c6a0ad7a9260c4cb02c707a0d813d17b23d9289 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Jan 2023 12:36:22 +1030 Subject: [PATCH 06/16] tcon: fix warning when it's used with NULL on some gcc versions. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Seems to be happening with gcc 12.2.0-3ubuntu1: ``` lightningd/jsonrpc.c: In function ‘destroy_json_command’: lightningd/jsonrpc.c:1180:63: error: the comparison will always evaluate as ‘false’ for the address of ‘canary’ will never be NULL [-Werror=address] lightningd/jsonrpc.c:108:53: note: ‘canary’ declared here ``` Signed-off-by: Rusty Russell --- ccan/tcon/tcon.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 *) -- 2.39.2 From 4c4de2df2c65453b8ca9592ccd21ce1b50f216a7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Jun 2022 18:02:09 +0930 Subject: [PATCH 07/16] ungraph: new module Signed-off-by: Rusty Russell --- ccan/ungraph/LICENSE | 1 + ccan/ungraph/_info | 77 +++++ ccan/ungraph/test/run.c | 140 ++++++++ ccan/ungraph/ungraph.c | 721 ++++++++++++++++++++++++++++++++++++++++ ccan/ungraph/ungraph.h | 53 +++ 5 files changed, 992 insertions(+) create mode 120000 ccan/ungraph/LICENSE create mode 100644 ccan/ungraph/_info create mode 100644 ccan/ungraph/test/run.c create mode 100644 ccan/ungraph/ungraph.c create mode 100644 ccan/ungraph/ungraph.h 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 */ -- 2.39.2 From 5465da47e3e0f2d09dc34b27cf2f3bc028478df4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 22 Mar 2023 12:42:18 +1030 Subject: [PATCH 08/16] tal: remove undefined behavior. In particular, we were casting between struct prop_hdr ** and char **: fixing this properly was quite a rework. And in one case we were using a pre-realloc pointer in tests: though to be fair, we tested it for equality. But ubsan got upset anyway. Signed-off-by: Rusty Russell --- ccan/tal/tal.c | 207 ++++++++++++++++++++++------------- ccan/tal/test/run-notifier.c | 4 +- 2 files changed, 130 insertions(+), 81 deletions(-) diff --git a/ccan/tal/tal.c b/ccan/tal/tal.c index 31e8e2f6..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 *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; @@ -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) @@ -832,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"); @@ -873,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; @@ -904,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; @@ -914,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; @@ -938,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/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; -- 2.39.2 From d1eabfd6e29ce24cafda96b3e846773be16dc592 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 May 2023 12:33:07 +0930 Subject: [PATCH 09/16] runes: allow underscores in field names, as per Python runes 0.6 Suggested-by: @niftynei Signed-off-by: Rusty Russell --- ccan/rune/coding.c | 2 +- ccan/rune/test/run.c | 2 +- ccan/rune/test/test_vectors.csv | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ccan/rune/coding.c b/ccan/rune/coding.c index f4d11028..495d37c3 100644 --- a/ccan/rune/coding.c +++ b/ccan/rune/coding.c @@ -206,7 +206,7 @@ bool rune_condition_is_valid(enum rune_condition cond) size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen) { for (size_t i = 0; i < alternstrlen; i++) { - if (cispunct(alternstr[i])) + if (cispunct(alternstr[i]) && alternstr[i] != '_') return i; } return alternstrlen; diff --git a/ccan/rune/test/run.c b/ccan/rune/test/run.c index e5326552..d90b701c 100644 --- a/ccan/rune/test/run.c +++ b/ccan/rune/test/run.c @@ -43,7 +43,7 @@ int main(void) 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; diff --git a/ccan/rune/test/test_vectors.csv b/ccan/rune/test/test_vectors.csv index a8411693..880ea3c6 100644 --- a/ccan/rune/test/test_vectors.csv +++ b/ccan/rune/test/test_vectors.csv @@ -94,6 +94,12 @@ 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 @@ -144,7 +150,6 @@ MALFORMED,Bad condition ?,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87 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== -- 2.39.2 From e3c3e3f3e965d80ac76cff6f506971f596efa1b6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 20 Jun 2023 12:30:32 +0930 Subject: [PATCH 10/16] configurator: increase stack size in HAVE_POINTER_SAFE_MAKECONTEXT test This was *hanging* on GitHub's CI (Ubuntu 22.04) with clang. The increase seems to have fixed it. Signed-off-by: Rusty Russell --- tools/configurator/configurator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/configurator/configurator.c b/tools/configurator/configurator.c index 722a6f69..7d8f6b09 100644 --- a/tools/configurator/configurator.c +++ b/tools/configurator/configurator.c @@ -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" -- 2.39.2 From 322f249409838d2547449ce0ee8b479242ce2136 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Apr 2023 15:41:13 +0930 Subject: [PATCH 11/16] ccan: add test that second arg in multiple arg string is correctly reported. Signed-off-by: Rusty Russell --- ccan/opt/test/run-correct-reporting.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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(); -- 2.39.2 From 272b81be35908402abc92222ebfa2b845c240319 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 May 2023 10:57:41 +0930 Subject: [PATCH 12/16] ccan: break out routine to parse a single long argument. Core Lightning (ab)uses opt for config file parsing; this makes that quite a bit neater! Signed-off-by: Rusty Russell --- ccan/opt/opt.h | 18 ++++++++++ ccan/opt/parse.c | 92 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 6f4b9dda..4b5a2c6c 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -509,6 +509,24 @@ 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); + /* Below here are private declarations. */ /* You can use this directly to build tables, but the macros will ensure * consistency and type safety. */ diff --git a/ccan/opt/parse.c b/ccan/opt/parse.c index d227f7bc..56c2a35b 100644 --- a/ccan/opt/parse.c +++ b/ccan/opt/parse.c @@ -14,7 +14,8 @@ /tmp/opt-example: option requires an argument -- 's' */ static int parse_err(void (*errlog)(const char *fmt, ...), - const char *argv0, const char *arg, unsigned len, + const char *argv0, + const char *arg, unsigned len, const char *problem) { errlog("%s: %.*s: %s", argv0, len, arg, problem); @@ -28,13 +29,63 @@ static void consume_option(int *argc, char *argv[], unsigned optnum) (*argc)--; } +/* This sets the len and o to indicate how far it is into the + * opt_table's names field. */ +static struct opt_table *opt_find_long_extra(const char *arg, + const char **optarg, + unsigned int *len, + const char **o) +{ + unsigned i; + + *optarg = NULL; + for (*o = first_lopt(&i, len); + *o; + *o = next_lopt(*o, &i, len)) { + if (strncmp(arg, *o, *len) != 0) + continue; + if (arg[*len] == '=') + *optarg = arg + *len + 1; + else if (arg[*len] != '\0') + continue; + return &opt_table[i]; + + } + return NULL; +} + +struct opt_table *opt_find_long(const char *arg, const char **optarg) +{ + unsigned len; + const char *o; + + return opt_find_long_extra(arg, optarg ? optarg : &o, &len, &o); +} + +static struct opt_table *opt_find_short_extra(char arg, const char **o) +{ + unsigned i; + for (*o = first_sopt(&i); *o; *o = next_sopt(*o, &i)) { + if (arg == **o) + return &opt_table[i]; + } + return NULL; +} + +struct opt_table *opt_find_short(char arg) +{ + const char *o; + return opt_find_short_extra(arg, &o); +} + /* Returns 1 if argument consumed, 0 if all done, -1 on error. */ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset, void (*errlog)(const char *fmt, ...), bool unknown_ok) { - unsigned i, arg, len; + unsigned arg, len; const char *o, *optarg = NULL; char *problem = NULL; + struct opt_table *ot; if (getenv("POSIXLY_CORRECT")) { /* Don't find options after non-options. */ @@ -58,34 +109,22 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset, /* Long options start with -- */ if (argv[arg][1] == '-') { assert(*offset == 0); - for (o = first_lopt(&i, &len); o; o = next_lopt(o, &i, &len)) { - if (strncmp(argv[arg] + 2, o, len) != 0) - continue; - if (argv[arg][2 + len] == '=') - optarg = argv[arg] + 2 + len + 1; - else if (argv[arg][2 + len] != '\0') - continue; - break; - } - if (!o) { + + ot = opt_find_long_extra(argv[arg]+2, &optarg, &len, &o); + if (!ot) { if (unknown_ok) goto ok; return parse_err(errlog, argv[0], argv[arg], strlen(argv[arg]), "unrecognized option"); } + /* For error messages, we include the leading '--' */ o -= 2; len += 2; } else { - /* offset allows us to handle -abc */ - for (o = first_sopt(&i); o; o = next_sopt(o, &i)) { - if (argv[arg][*offset + 1] != *o) - continue; - (*offset)++; - break; - } - if (!o) { + ot = opt_find_short_extra(argv[arg][*offset + 1], &o); + if (!ot) { if (unknown_ok) { (*offset)++; goto ok; @@ -94,17 +133,19 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset, argv[arg], strlen(argv[arg]), "unrecognized option"); } + + (*offset)++; /* For error messages, we include the leading '-' */ o--; len = 2; } - if ((opt_table[i].type & ~OPT_EARLY) == OPT_NOARG) { + if ((ot->type & ~OPT_EARLY) == 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) { -- 2.39.2 From 4bfa8587c9b7c8450320232837c6481d07ce77f6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 May 2023 11:00:22 +0930 Subject: [PATCH 13/16] ccan: allow user to set some bits in opt_table.type. In particular, Core Lightning wants to use this to flag arguments which can be specified multiple times (without overriding). Signed-off-by: Rusty Russell --- ccan/opt/opt.c | 15 ++++----- ccan/opt/opt.h | 11 +++++++ ccan/opt/parse.c | 2 +- ccan/opt/test/run-userbits.c | 59 ++++++++++++++++++++++++++++++++++++ ccan/opt/usage.c | 6 ++-- 5 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 ccan/opt/test/run-userbits.c diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index d376a598..ef711d2c 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); @@ -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 4b5a2c6c..9eec7385 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -527,6 +527,12 @@ struct opt_table *opt_find_long(const char *arg, const char **optarg); */ 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_EARLY) == OPT_NOARG) { + if (ot->type & OPT_NOARG) { if (optarg) return parse_err(errlog, argv[0], o, len, "doesn't allow an argument"); 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/usage.c b/ccan/opt/usage.c index 8ee4ebd0..4ed27919 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -182,10 +182,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(" "); @@ -221,7 +221,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; -- 2.39.2 From 995d2aff223e70ef5c60ac0416645b27ac84d5ff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 May 2023 11:00:26 +0930 Subject: [PATCH 14/16] ccan: update show callback to take explicit length. For now we still use 80, but we could vary that in future. Signed-off-by: Rusty Russell --- ccan/opt/helpers.c | 121 ++++++++++++++-------------- ccan/opt/opt.c | 2 +- ccan/opt/opt.h | 61 +++++++------- ccan/opt/test/run-add_desc.c | 4 +- ccan/opt/test/run-helpers.c | 150 +++++++++++++++++------------------ ccan/opt/test/utils.c | 4 +- ccan/opt/test/utils.h | 2 +- ccan/opt/usage.c | 5 +- 8 files changed, 176 insertions(+), 173 deletions(-) diff --git a/ccan/opt/helpers.c b/ccan/opt/helpers.c index 118e5436..0a6cb345 100644 --- a/ccan/opt/helpers.c +++ b/ccan/opt/helpers.c @@ -138,10 +138,10 @@ char *opt_set_floatval(const char *arg, float *f) return NULL; } -void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f) +void 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); } char *opt_set_doubleval(const char *arg, double *d) @@ -160,9 +160,9 @@ char *opt_set_doubleval(const char *arg, double *d) return NULL; } -void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d) +void opt_show_doubleval(char *buf, size_t len, const double *d) { - snprintf(buf, OPT_SHOW_LEN, "%f", *d); + snprintf(buf, len, "%f", *d); } char *opt_inc_intval(int *i) @@ -196,52 +196,54 @@ char *opt_usage_and_exit(const char *extra) exit(0); } -void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b) +void 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); } -void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b) +void 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); } -void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p) +void 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; 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'; + if (plen > len - 2) + plen = len - 2; + strncpy(buf+1, *p, plen); + buf[1+plen] = '"'; + if (plen < len - 2) + buf[2+plen] = '\0'; } else { - strncpy(buf, "(nil)", OPT_SHOW_LEN); + strncpy(buf, "(nil)", len); } } /* Show an integer value, various forms. */ -void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i) +void opt_show_intval(char *buf, size_t len, const int *i) { - snprintf(buf, OPT_SHOW_LEN, "%i", *i); + snprintf(buf, len, "%i", *i); } -void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui) +void opt_show_uintval(char *buf, size_t len, const unsigned int *ui) { - snprintf(buf, OPT_SHOW_LEN, "%u", *ui); + snprintf(buf, len, "%u", *ui); } -void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l) +void opt_show_longval(char *buf, size_t len, const long *l) { - snprintf(buf, OPT_SHOW_LEN, "%li", *l); + snprintf(buf, len, "%li", *l); } -void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul) +void opt_show_ulongval(char *buf, size_t len, const unsigned long *ul) { - snprintf(buf, OPT_SHOW_LEN, "%lu", *ul); + snprintf(buf, len, "%lu", *ul); } /* a helper function that multiplies out an argument's kMGTPE suffix in the @@ -447,14 +449,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 +466,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 +489,72 @@ 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) +void 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); } -void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x) +void 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); } -void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x) +void 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); } /* _bi, unsigned */ -void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x) +void 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); } -void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x) +void 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); } -void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x) +void 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); } /* _si, signed */ -void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x) +void 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); } -void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x) +void 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); } -void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x) +void 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); } /* _si, unsigned */ -void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x) +void 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); } -void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x) +void 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); } -void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x) +void 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); } diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index ef711d2c..09c85081 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -162,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), + void (*show)(char *buf, size_t len, const void *arg), const void *arg, const char *desc) { struct opt_table opt; diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 9eec7385..9c9f337d 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -47,10 +47,10 @@ 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 "void show(char *, + * size_t len, const type *)". It should write up to len bytes into the first + * argument; 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 +429,37 @@ 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); +void 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); +void 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); +void 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); +void 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); +void 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); +void 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); +void 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); +void 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); +void 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 +479,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); +void opt_show_intval_bi(char *buf, size_t len, const int *x); +void opt_show_longval_bi(char *buf, size_t len, const long *x); +void opt_show_longlongval_bi(char *buf, size_t len, const long long *x); +void opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x); +void opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x); +void 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); +void opt_show_intval_si(char *buf, size_t len, const int *x); +void opt_show_longval_si(char *buf, size_t len, const long *x); +void opt_show_longlongval_si(char *buf, size_t len, const long long *x); +void opt_show_uintval_si(char *buf, size_t len, const unsigned int *x); +void opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x); +void opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x); @@ -554,7 +551,7 @@ struct opt_table { enum opt_type type; char *(*cb)(void *arg); /* OPT_NOARG */ char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */ - void (*show)(char buf[OPT_SHOW_LEN], const void *arg); + void (*show)(char *buf, size_t len, const void *arg); union { const void *carg; void *arg; @@ -580,14 +577,14 @@ struct opt_table { char *(*)(const char *, const typeof(*(arg))*), \ char *(*)(const char *, const void *), \ (cb)), \ - typesafe_cb_cast(void (*)(char buf[], const void *), \ - void (*)(char buf[], const typeof(*(arg))*), (show)) + typesafe_cb_cast(void (*)(char *buf, size_t, const void *), \ + void (*)(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), + void (*show)(char *buf, size_t len, const void *arg), const void *arg, const char *desc); /* We use this to get typechecking for OPT_SUBTABLE */ diff --git a/ccan/opt/test/run-add_desc.c b/ccan/opt/test/run-add_desc.c index b559c7f7..ddec6f13 100644 --- a/ccan/opt/test/run-add_desc.c +++ b/ccan/opt/test/run-add_desc.c @@ -4,13 +4,13 @@ #include #include -static void show_10(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED) +static void show_10(char *buf, size_t len, const void *arg UNNEEDED) { memset(buf, 'X', 10); buf[10] = '\0'; } -static void show_max(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED) +static void show_max(char *buf, size_t len, const void *arg UNNEEDED) { memset(buf, 'X', OPT_SHOW_LEN); } 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/utils.c b/ccan/opt/test/utils.c index 2ff04884..700748be 100644 --- a/ccan/opt/test/utils.c +++ b/ccan/opt/test/utils.c @@ -21,9 +21,9 @@ char *test_arg(const char *optarg, const char *arg) return NULL; } -void show_arg(char buf[OPT_SHOW_LEN], const char *arg) +void show_arg(char *buf, size_t len, const char *arg) { - strncpy(buf, arg, OPT_SHOW_LEN); + strncpy(buf, arg, len); } char *err_output = NULL; diff --git a/ccan/opt/test/utils.h b/ccan/opt/test/utils.h index 12cf0b75..64641ec4 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); +void 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 4ed27919..e9629838 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; @@ -148,7 +151,7 @@ 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); + 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(")") -- 2.39.2 From 3beff01ae4dfb8b843bf5e78905fb8bc434cb270 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 May 2023 11:00:31 +0930 Subject: [PATCH 15/16] opt: allow show callbacks to return false. Sometimes, arguments are optional, so it's useful to decide at runtime whether to print a default in --help. Signed-off-by: Rusty Russell --- ccan/opt/helpers.c | 70 +++++++++++++++++++++++------------- ccan/opt/opt.c | 2 +- ccan/opt/opt.h | 58 +++++++++++++++--------------- ccan/opt/test/run-add_desc.c | 23 ++++++++++-- ccan/opt/test/utils.c | 3 +- ccan/opt/test/utils.h | 2 +- ccan/opt/usage.c | 24 ++++++------- 7 files changed, 111 insertions(+), 71 deletions(-) diff --git a/ccan/opt/helpers.c b/ccan/opt/helpers.c index 0a6cb345..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, size_t len, const float *f) +bool opt_show_floatval(char *buf, size_t len, const float *f) { double d = *f; 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, size_t len, const double *d) +bool opt_show_doubleval(char *buf, size_t len, const double *d) { snprintf(buf, len, "%f", *d); + return true; } char *opt_inc_intval(int *i) @@ -196,22 +198,24 @@ char *opt_usage_and_exit(const char *extra) exit(0); } -void opt_show_bool(char *buf, size_t len, const bool *b) +bool opt_show_bool(char *buf, size_t len, const bool *b) { strncpy(buf, *b ? "true" : "false", len); + return true; } -void opt_show_invbool(char *buf, size_t len, const bool *b) +bool opt_show_invbool(char *buf, size_t len, const bool *b) { strncpy(buf, *b ? "false" : "true", len); + return true; } -void opt_show_charp(char *buf, size_t len, char *const *p) +bool opt_show_charp(char *buf, size_t len, char *const *p) { if (*p) { size_t plen = strlen(*p); if (len < 2) - return; + return false; buf[0] = '"'; if (plen > len - 2) plen = len - 2; @@ -219,31 +223,35 @@ void opt_show_charp(char *buf, size_t len, char *const *p) buf[1+plen] = '"'; if (plen < len - 2) buf[2+plen] = '\0'; - } - else { - strncpy(buf, "(nil)", len); + return true; + } else { + return false; } } /* Show an integer value, various forms. */ -void opt_show_intval(char *buf, size_t len, const int *i) +bool opt_show_intval(char *buf, size_t len, const int *i) { snprintf(buf, len, "%i", *i); + return true; } -void opt_show_uintval(char *buf, size_t len, const unsigned int *ui) +bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui) { snprintf(buf, len, "%u", *ui); + return true; } -void opt_show_longval(char *buf, size_t len, const long *l) +bool opt_show_longval(char *buf, size_t len, const long *l) { snprintf(buf, len, "%li", *l); + return true; } -void opt_show_ulongval(char *buf, size_t len, const unsigned long *ul) +bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul) { snprintf(buf, len, "%lu", *ul); + return true; } /* a helper function that multiplies out an argument's kMGTPE suffix in the @@ -495,66 +503,78 @@ static void show_ullong_with_suffix(char *buf, size_t len, } /* _bi, signed */ -void opt_show_intval_bi(char *buf, size_t len, const int *x) +bool opt_show_intval_bi(char *buf, size_t len, const int *x) { show_llong_with_suffix(buf, len, *x, 1024); + return true; } -void opt_show_longval_bi(char *buf, size_t len, const long *x) +bool opt_show_longval_bi(char *buf, size_t len, const long *x) { show_llong_with_suffix(buf, len, *x, 1024); + return true; } -void opt_show_longlongval_bi(char *buf, size_t len, const long long *x) +bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x) { show_llong_with_suffix(buf, len, *x, 1024); + return true; } /* _bi, unsigned */ -void opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x) +bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } -void opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x) +bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } -void opt_show_ulonglongval_bi(char *buf, size_t 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, len, (unsigned long long) *x, 1024); + return true; } /* _si, signed */ -void opt_show_intval_si(char *buf, size_t len, const int *x) +bool opt_show_intval_si(char *buf, size_t len, const int *x) { show_llong_with_suffix(buf, len, (long long) *x, 1000); + return true; } -void opt_show_longval_si(char *buf, size_t len, const long *x) +bool opt_show_longval_si(char *buf, size_t len, const long *x) { show_llong_with_suffix(buf, len, (long long) *x, 1000); + return true; } -void opt_show_longlongval_si(char *buf, size_t len, const long long *x) +bool opt_show_longlongval_si(char *buf, size_t len, const long long *x) { show_llong_with_suffix(buf, len, *x, 1000); + return true; } /* _si, unsigned */ -void opt_show_uintval_si(char *buf, size_t len, const unsigned int *x) +bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } -void opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x) +bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } -void opt_show_ulonglongval_si(char *buf, size_t 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, len, (unsigned long long) *x, 1000); + return true; } diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index 09c85081..9149374c 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -162,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, size_t len, const void *arg), + bool (*show)(char *buf, size_t len, const void *arg), const void *arg, const char *desc) { struct opt_table opt; diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 9c9f337d..e0331be2 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -47,9 +47,10 @@ 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 *, - * size_t len, const type *)". It should write up to len bytes into the first - * argument; unless it uses the entire len bytes it should nul-terminate that + * 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, @@ -434,32 +435,33 @@ extern const char opt_hidden[]; 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, size_t 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, size_t 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, size_t 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, size_t 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, size_t 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, size_t 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, size_t 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, size_t 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, size_t 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 @@ -479,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, size_t len, const int *x); -void opt_show_longval_bi(char *buf, size_t len, const long *x); -void opt_show_longlongval_bi(char *buf, size_t len, const long long *x); -void opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x); -void opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x); -void opt_show_ulonglongval_bi(char *buf, size_t 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, size_t len, const int *x); -void opt_show_longval_si(char *buf, size_t len, const long *x); -void opt_show_longlongval_si(char *buf, size_t len, const long long *x); -void opt_show_uintval_si(char *buf, size_t len, const unsigned int *x); -void opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x); -void opt_show_ulonglongval_si(char *buf, size_t 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); @@ -551,7 +553,7 @@ struct opt_table { enum opt_type type; char *(*cb)(void *arg); /* OPT_NOARG */ char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */ - void (*show)(char *buf, size_t len, const void *arg); + bool (*show)(char *buf, size_t len, const void *arg); union { const void *carg; void *arg; @@ -577,14 +579,14 @@ struct opt_table { char *(*)(const char *, const typeof(*(arg))*), \ char *(*)(const char *, const void *), \ (cb)), \ - typesafe_cb_cast(void (*)(char *buf, size_t, const void *), \ - void (*)(char *buf, size_t, 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, size_t 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 */ diff --git a/ccan/opt/test/run-add_desc.c b/ccan/opt/test/run-add_desc.c index ddec6f13..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, size_t 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, size_t 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/utils.c b/ccan/opt/test/utils.c index 700748be..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, size_t len, const char *arg) +bool show_arg(char *buf, size_t len, const char *arg) { 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 64641ec4..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, size_t 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 e9629838..568e4661 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -151,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_SHOW_LEN, 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; } -- 2.39.2 From cd56b18ae87de6981b88abbe52544d8cc9f8aa0e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 1 Aug 2023 11:13:53 +0930 Subject: [PATCH 16/16] base64: fix for unsigned chars (e.g. ARM). ``` ccan/ccan/base64/base64.c:34:10: error: result of comparison of constant 255 with expression of type 'int8_t' (aka 'signed char') is always false [-Werror,-Wtautological-constant-out-of-range-compare] if (ret == (char)0xff) { ~~~ ^ ~~~~~~~~~~ ccan/ccan/base64/base64.c:44:57: error: result of comparison of constant 255 with expression of type 'const signed char' is always true [-Werror,-Wtautological-constant-out-of-range-compare] return (maps->decode_map[(const unsigned char)b64char] != (char)0xff); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~ ``` Reported-by: Christian Decker Signed-off-by: Rusty Russell --- ccan/base64/base64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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]) -- 2.39.2