From fd8e571f5dfa09db184ed6e5557ac735278aefbd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Jan 2023 12:50:22 +1030 Subject: [PATCH 01/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 02/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 03/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 04/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 05/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 06/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 07/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 08/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 09/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 10/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 11/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 12/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 13/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 14/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 15/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 From 995e75dd41d24edc41a03b654e035009a86b26fc Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 1 Jun 2024 19:03:27 +1000 Subject: [PATCH 16/16] bitmap: Avoid shadowing type name with parameter name 'bitmap' is a typedef name in this module, and we also use it as a parameter name in a bunch of places. Although that's possible, because types and variables live in different namespaces, it's probably not that wise. It also appears to trip warnings with at least some options and compiler versions. Reported-by: Ashok Raj Signed-off-by: David Gibson --- ccan/bitmap/bitmap.c | 30 ++++++++++----------- ccan/bitmap/bitmap.h | 63 ++++++++++++++++++++++---------------------- 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/ccan/bitmap/bitmap.c b/ccan/bitmap/bitmap.c index d254b20e..ccc0cd21 100644 --- a/ccan/bitmap/bitmap.c +++ b/ccan/bitmap/bitmap.c @@ -9,7 +9,7 @@ #define BIT_ALIGN_DOWN(n) ((n) & ~(BITMAP_WORD_BITS - 1)) #define BIT_ALIGN_UP(n) BIT_ALIGN_DOWN((n) + BITMAP_WORD_BITS - 1) -void bitmap_zero_range(bitmap *bitmap, unsigned long n, unsigned long m) +void bitmap_zero_range(bitmap *b, unsigned long n, unsigned long m) { unsigned long an = BIT_ALIGN_UP(n); unsigned long am = BIT_ALIGN_DOWN(m); @@ -19,22 +19,22 @@ void bitmap_zero_range(bitmap *bitmap, unsigned long n, unsigned long m) assert(m >= n); if (am < an) { - BITMAP_WORD(bitmap, n) &= ~bitmap_bswap(headmask & tailmask); + BITMAP_WORD(b, n) &= ~bitmap_bswap(headmask & tailmask); return; } if (an > n) - BITMAP_WORD(bitmap, n) &= ~bitmap_bswap(headmask); + BITMAP_WORD(b, n) &= ~bitmap_bswap(headmask); if (am > an) - memset(&BITMAP_WORD(bitmap, an), 0, + memset(&BITMAP_WORD(b, an), 0, (am - an) / BITMAP_WORD_BITS * sizeof(bitmap_word)); if (m > am) - BITMAP_WORD(bitmap, m) &= ~bitmap_bswap(tailmask); + BITMAP_WORD(b, m) &= ~bitmap_bswap(tailmask); } -void bitmap_fill_range(bitmap *bitmap, unsigned long n, unsigned long m) +void bitmap_fill_range(bitmap *b, unsigned long n, unsigned long m) { unsigned long an = BIT_ALIGN_UP(n); unsigned long am = BIT_ALIGN_DOWN(m); @@ -44,19 +44,19 @@ void bitmap_fill_range(bitmap *bitmap, unsigned long n, unsigned long m) assert(m >= n); if (am < an) { - BITMAP_WORD(bitmap, n) |= bitmap_bswap(headmask & tailmask); + BITMAP_WORD(b, n) |= bitmap_bswap(headmask & tailmask); return; } if (an > n) - BITMAP_WORD(bitmap, n) |= bitmap_bswap(headmask); + BITMAP_WORD(b, n) |= bitmap_bswap(headmask); if (am > an) - memset(&BITMAP_WORD(bitmap, an), 0xff, + memset(&BITMAP_WORD(b, an), 0xff, (am - an) / BITMAP_WORD_BITS * sizeof(bitmap_word)); if (m > am) - BITMAP_WORD(bitmap, m) |= bitmap_bswap(tailmask); + BITMAP_WORD(b, m) |= bitmap_bswap(tailmask); } static int bitmap_clz(bitmap_word w) @@ -76,7 +76,7 @@ static int bitmap_clz(bitmap_word w) #endif } -unsigned long bitmap_ffs(const bitmap *bitmap, +unsigned long bitmap_ffs(const bitmap *b, unsigned long n, unsigned long m) { unsigned long an = BIT_ALIGN_UP(n); @@ -87,7 +87,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap, assert(m >= n); if (am < an) { - bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, n)); + bitmap_word w = bitmap_bswap(BITMAP_WORD(b, n)); w &= (headmask & tailmask); @@ -95,7 +95,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap, } if (an > n) { - bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, n)); + bitmap_word w = bitmap_bswap(BITMAP_WORD(b, n)); w &= headmask; @@ -104,7 +104,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap, } while (an < am) { - bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, an)); + bitmap_word w = bitmap_bswap(BITMAP_WORD(b, an)); if (w) return an + bitmap_clz(w); @@ -113,7 +113,7 @@ unsigned long bitmap_ffs(const bitmap *bitmap, } if (m > am) { - bitmap_word w = bitmap_bswap(BITMAP_WORD(bitmap, m)); + bitmap_word w = bitmap_bswap(BITMAP_WORD(b, m)); w &= tailmask; diff --git a/ccan/bitmap/bitmap.h b/ccan/bitmap/bitmap.h index 54382801..e1bf4bb7 100644 --- a/ccan/bitmap/bitmap.h +++ b/ccan/bitmap/bitmap.h @@ -58,37 +58,37 @@ static inline bitmap_word bitmap_bswap(bitmap_word w) #define BITMAP_TAIL(_bm, _nbits) \ (BITMAP_TAILWORD(_bm, _nbits) & BITMAP_TAILBITS(_nbits)) -static inline void bitmap_set_bit(bitmap *bitmap, unsigned long n) +static inline void bitmap_set_bit(bitmap *b, unsigned long n) { - BITMAP_WORD(bitmap, n) |= BITMAP_WORDBIT(n); + BITMAP_WORD(b, n) |= BITMAP_WORDBIT(n); } -static inline void bitmap_clear_bit(bitmap *bitmap, unsigned long n) +static inline void bitmap_clear_bit(bitmap *b, unsigned long n) { - BITMAP_WORD(bitmap, n) &= ~BITMAP_WORDBIT(n); + BITMAP_WORD(b, n) &= ~BITMAP_WORDBIT(n); } -static inline void bitmap_change_bit(bitmap *bitmap, unsigned long n) +static inline void bitmap_change_bit(bitmap *b, unsigned long n) { - BITMAP_WORD(bitmap, n) ^= BITMAP_WORDBIT(n); + BITMAP_WORD(b, n) ^= BITMAP_WORDBIT(n); } -static inline bool bitmap_test_bit(const bitmap *bitmap, unsigned long n) +static inline bool bitmap_test_bit(const bitmap *b, unsigned long n) { - return !!(BITMAP_WORD(bitmap, n) & BITMAP_WORDBIT(n)); + return !!(BITMAP_WORD(b, n) & BITMAP_WORDBIT(n)); } -void bitmap_zero_range(bitmap *bitmap, unsigned long n, unsigned long m); -void bitmap_fill_range(bitmap *bitmap, unsigned long n, unsigned long m); +void bitmap_zero_range(bitmap *b, unsigned long n, unsigned long m); +void bitmap_fill_range(bitmap *b, unsigned long n, unsigned long m); -static inline void bitmap_zero(bitmap *bitmap, unsigned long nbits) +static inline void bitmap_zero(bitmap *b, unsigned long nbits) { - memset(bitmap, 0, bitmap_sizeof(nbits)); + memset(b, 0, bitmap_sizeof(nbits)); } -static inline void bitmap_fill(bitmap *bitmap, unsigned long nbits) +static inline void bitmap_fill(bitmap *b, unsigned long nbits) { - memset(bitmap, 0xff, bitmap_sizeof(nbits)); + memset(b, 0xff, bitmap_sizeof(nbits)); } static inline void bitmap_copy(bitmap *dst, const bitmap *src, @@ -161,37 +161,36 @@ static inline bool bitmap_subset(const bitmap *src1, const bitmap *src2, return true; } -static inline bool bitmap_full(const bitmap *bitmap, unsigned long nbits) +static inline bool bitmap_full(const bitmap *b, unsigned long nbits) { unsigned long i; for (i = 0; i < BITMAP_HEADWORDS(nbits); i++) { - if (bitmap[i].w != -1UL) + if (b[i].w != -1UL) return false; } if (BITMAP_HASTAIL(nbits) && - (BITMAP_TAIL(bitmap, nbits) != BITMAP_TAILBITS(nbits))) + (BITMAP_TAIL(b, nbits) != BITMAP_TAILBITS(nbits))) return false; return true; } -static inline bool bitmap_empty(const bitmap *bitmap, unsigned long nbits) +static inline bool bitmap_empty(const bitmap *b, unsigned long nbits) { unsigned long i; for (i = 0; i < BITMAP_HEADWORDS(nbits); i++) { - if (bitmap[i].w != 0) + if (b[i].w != 0) return false; } - if (BITMAP_HASTAIL(nbits) && (BITMAP_TAIL(bitmap, nbits) != 0)) + if (BITMAP_HASTAIL(nbits) && (BITMAP_TAIL(b, nbits) != 0)) return false; return true; } -unsigned long bitmap_ffs(const bitmap *bitmap, - unsigned long n, unsigned long m); +unsigned long bitmap_ffs(const bitmap *b, unsigned long n, unsigned long m); /* * Allocation functions @@ -221,26 +220,26 @@ static inline bitmap *bitmap_alloc1(unsigned long nbits) return bitmap; } -static inline bitmap *bitmap_realloc0(bitmap *bitmap, +static inline bitmap *bitmap_realloc0(bitmap *b, unsigned long obits, unsigned long nbits) { - bitmap = realloc(bitmap, bitmap_sizeof(nbits)); + b = realloc(b, bitmap_sizeof(nbits)); - if ((nbits > obits) && bitmap) - bitmap_zero_range(bitmap, obits, nbits); + if ((nbits > obits) && b) + bitmap_zero_range(b, obits, nbits); - return bitmap; + return b; } -static inline bitmap *bitmap_realloc1(bitmap *bitmap, +static inline bitmap *bitmap_realloc1(bitmap *b, unsigned long obits, unsigned long nbits) { - bitmap = realloc(bitmap, bitmap_sizeof(nbits)); + b = realloc(b, bitmap_sizeof(nbits)); - if ((nbits > obits) && bitmap) - bitmap_fill_range(bitmap, obits, nbits); + if ((nbits > obits) && b) + bitmap_fill_range(b, obits, nbits); - return bitmap; + return b; } #endif /* CCAN_BITMAP_H_ */ -- 2.39.2