lnvis

nanovg lightning network visualizer
git clone git://jb55.com/lnvis
Log | Files | Refs | README | LICENSE

commit dd883c9e4093647b9dc7ac748fa2e9bcf1c5e312
parent 4be8e791a44dcbb0a835fe501351b23d1ca1644b
Author: William Casarin <jb55@jb55.com>
Date:   Mon, 20 Aug 2018 20:54:31 -0700

options!

Diffstat:
MMakefile | 1+
Adeps/commander/commander.c | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adeps/commander/commander.h | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adeps/commander/package.json | 9+++++++++
Mln.c | 6+++++-
Mmain.c | 18++++++++----------
Aoptions.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aoptions.h | 15+++++++++++++++
8 files changed, 443 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile @@ -50,6 +50,7 @@ SRCS += render.c SRCS += perf.c SRCS += json.c SRCS += demo.c +SRCS += options.c SRCS += $(wildcard deps/*.c) SRCS += $(wildcard deps/*/*.c) diff --git a/deps/commander/commander.c b/deps/commander/commander.c @@ -0,0 +1,269 @@ + +// +// commander.c +// +// Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "commander.h" + +/* + * Output error and exit. + */ + +static void +error(char *msg) { + fprintf(stderr, "%s\n", msg); + exit(1); +} + +/* + * Output command version. + */ + +static void +command_version(command_t *self) { + printf("%s\n", self->version); + command_free(self); + exit(0); +} + +/* + * Output command help. + */ + +void +command_help(command_t *self) { + printf("\n"); + printf(" Usage: %s %s\n", self->name, self->usage); + printf("\n"); + printf(" Options:\n"); + printf("\n"); + + int i; + for (i = 0; i < self->option_count; ++i) { + command_option_t *option = &self->options[i]; + printf(" %s, %-25s %s\n" + , option->small + , option->large_with_arg + , option->description); + } + + printf("\n"); + command_free(self); + exit(0); +} + +/* + * Initialize with program `name` and `version`. + */ + +void +command_init(command_t *self, const char *name, const char *version) { + self->arg = NULL; + self->name = name; + self->version = version; + self->option_count = self->argc = 0; + self->usage = "[options]"; + self->nargv = NULL; + command_option(self, "-V", "--version", "output program version", command_version); + command_option(self, "-h", "--help", "output help information", command_help); +} + +/* + * Free up commander after use. + */ + +void +command_free(command_t *self) { + int i; + + for (i = 0; i < self->option_count; ++i) { + command_option_t *option = &self->options[i]; + free(option->argname); + free(option->large); + } + + if (self->nargv) { + for (i = 0; self->nargv[i]; ++i) { + free(self->nargv[i]); + } + free(self->nargv); + } +} + +/* + * Parse argname from `str`. For example + * Take "--required <arg>" and populate `flag` + * with "--required" and `arg` with "<arg>". + */ + +static void +parse_argname(const char *str, char *flag, char *arg) { + int buffer = 0; + size_t flagpos = 0; + size_t argpos = 0; + size_t len = strlen(str); + size_t i; + + for (i = 0; i < len; ++i) { + if (buffer || '[' == str[i] || '<' == str[i]) { + buffer = 1; + arg[argpos++] = str[i]; + } else { + if (' ' == str[i]) continue; + flag[flagpos++] = str[i]; + } + } + + arg[argpos] = '\0'; + flag[flagpos] = '\0'; +} + +/* + * Normalize the argument vector by exploding + * multiple options (if any). For example + * "foo -abc --scm git" -> "foo -a -b -c --scm git" + */ + +static char ** +normalize_args(int *argc, char **argv) { + int size = 0; + int alloc = *argc + 1; + char **nargv = malloc(alloc * sizeof(char *)); + int i; + + for (i = 0; argv[i]; ++i) { + const char *arg = argv[i]; + size_t len = strlen(arg); + + // short flag + if (len > 2 && '-' == arg[0] && !strchr(arg + 1, '-')) { + alloc += len - 2; + nargv = realloc(nargv, alloc * sizeof(char *)); + for (size_t j = 1; j < len; ++j) { + nargv[size] = malloc(3); + sprintf(nargv[size], "-%c", arg[j]); + size++; + } + continue; + } + + // regular arg + nargv[size] = malloc(len + 1); + strcpy(nargv[size], arg); + size++; + } + + nargv[size] = NULL; + *argc = size; + return nargv; +} + +/* + * Define an option. + */ + +void +command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) { + if (self->option_count == COMMANDER_MAX_OPTIONS) { + command_free(self); + error("Maximum option definitions exceeded"); + } + int n = self->option_count++; + command_option_t *option = &self->options[n]; + option->cb = cb; + option->small = small; + option->description = desc; + option->required_arg = option->optional_arg = 0; + option->large_with_arg = large; + option->argname = malloc(strlen(large) + 1); + assert(option->argname); + option->large = malloc(strlen(large) + 1); + assert(option->large); + parse_argname(large, option->large, option->argname); + if ('[' == option->argname[0]) option->optional_arg = 1; + if ('<' == option->argname[0]) option->required_arg = 1; +} + +/* + * Parse `argv` (internal). + * Input arguments should be normalized first + * see `normalize_args`. + */ + +static void +command_parse_args(command_t *self, int argc, char **argv) { + int literal = 0; + int i, j; + + for (i = 1; i < argc; ++i) { + const char *arg = argv[i]; + for (j = 0; j < self->option_count; ++j) { + command_option_t *option = &self->options[j]; + + // match flag + if (!strcmp(arg, option->small) || !strcmp(arg, option->large)) { + self->arg = NULL; + + // required + if (option->required_arg) { + arg = argv[++i]; + if (!arg || '-' == arg[0]) { + fprintf(stderr, "%s %s argument required\n", option->large, option->argname); + command_free(self); + exit(1); + } + self->arg = arg; + } + + // optional + if (option->optional_arg) { + if (argv[i + 1] && '-' != argv[i + 1][0]) { + self->arg = argv[++i]; + } + } + + // invoke callback + option->cb(self); + goto match; + } + } + + // -- + if ('-' == arg[0] && '-' == arg[1] && 0 == arg[2]) { + literal = 1; + goto match; + } + + // unrecognized + if ('-' == arg[0] && !literal) { + fprintf(stderr, "unrecognized flag %s\n", arg); + command_free(self); + exit(1); + } + + int n = self->argc++; + if (n == COMMANDER_MAX_ARGS) { + command_free(self); + error("Maximum number of arguments exceeded"); + } + self->argv[n] = (char *) arg; + match:; + } +} + +/* + * Parse `argv` (public). + */ + +void +command_parse(command_t *self, int argc, char **argv) { + self->nargv = normalize_args(&argc, argv); + command_parse_args(self, argc, self->nargv); + self->argv[self->argc] = NULL; +} diff --git a/deps/commander/commander.h b/deps/commander/commander.h @@ -0,0 +1,88 @@ + +// +// commander.h +// +// Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> +// + +#ifndef COMMANDER_H +#define COMMANDER_H + +/* + * Max options that can be defined. + */ + +#ifndef COMMANDER_MAX_OPTIONS +#define COMMANDER_MAX_OPTIONS 32 +#endif + +/* + * Max arguments that can be passed. + */ + +#ifndef COMMANDER_MAX_ARGS +#define COMMANDER_MAX_ARGS 32 +#endif + +/* + * Command struct. + */ + +struct command; + +/* + * Option callback. + */ + +typedef void (* command_callback_t)(struct command *self); + +/* + * Command option. + */ + +typedef struct { + int optional_arg; + int required_arg; + char *argname; + char *large; + const char *small; + const char *large_with_arg; + const char *description; + command_callback_t cb; +} command_option_t; + +/* + * Command. + */ + +typedef struct command { + void *data; + const char *usage; + const char *arg; + const char *name; + const char *version; + int option_count; + command_option_t options[COMMANDER_MAX_OPTIONS]; + int argc; + char *argv[COMMANDER_MAX_ARGS]; + char **nargv; +} command_t; + +// prototypes + +void +command_init(command_t *self, const char *name, const char *version); + +void +command_free(command_t *self); + +void +command_help(command_t *self); + +void +command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb); + +void +command_parse(command_t *self, int argc, char **argv); + +#endif /* COMMANDER_H */ diff --git a/deps/commander/package.json b/deps/commander/package.json @@ -0,0 +1,9 @@ +{ + "name": "commander", + "version": "1.3.2", + "repo": "clibs/commander", + "description": "Command-line argument parser", + "keywords": ["cli", "command", "parser", "argv", "args", "options"], + "license": "MIT", + "src": ["src/commander.h", "src/commander.c"] +} diff --git a/ln.c b/ln.c @@ -116,6 +116,7 @@ static void print_node(struct node *node) void filter_network(const char *nodeid, struct node *filter_node, struct ln *ln) { u32 i; + int first = 1; struct node *node = NULL; struct channel *chan = NULL; ln->filter = (char*)nodeid; @@ -123,7 +124,10 @@ void filter_network(const char *nodeid, struct node *filter_node, struct ln *ln) for (i = 0; i < ln->node_count; i++) { node = &ln->nodes[i]; - if (filter_node == node || (nodeid && streq(nodeid, node->id))) { + if ((filter_node == NULL && first) || + filter_node == node || + (nodeid && streq(nodeid, node->id))) { + first = 0; node->visible = 1; ln->filter_target = node; } diff --git a/main.c b/main.c @@ -19,6 +19,7 @@ #include "update.h" #include "ln.h" #include "json.h" +#include "options.h" void errorcb(int error, const char *desc) { @@ -142,10 +143,10 @@ static void connect_node_channels(struct node *nodes, int node_count, } } -void test_read_json(struct ln *ln) +void read_json(struct ln *ln, struct options *options) { - FILE *channels_fd = fopen("clightning-channels.json", "r"); - FILE *nodes_fd = fopen("clightning-nodes.json", "r"); + FILE *channels_fd = fopen(options->channels, "r"); + FILE *nodes_fd = fopen(options->nodes, "r"); int channel_count = 0; int node_count = 0; @@ -185,7 +186,7 @@ int main(int argc, const char *argv[]) NVGcontext *vg = NULL; PerfGraph fps; double prevt = 0; - const char *filter; + struct options options; srand(0); // ln collision grid subdivision @@ -199,12 +200,9 @@ int main(int argc, const char *argv[]) | DISP_STROKE_NODES ; - if (argc == 2) - filter = argv[1]; - else - filter = "03f3c108ccd536b8526841f0a5c58212bb9e6584a1eb493080e7c1cc34f82dad71"; + parse_options(argc, argv, &options); - test_read_json(&ln); + read_json(&ln, &options); ln.display_flags = flags; @@ -305,7 +303,7 @@ int main(int argc, const char *argv[]) if (first) { /* random_network(winWidth, winHeight, 3, 500, &ln); */ init_network(winWidth, winHeight, &ln); - filter_network(filter, NULL, &ln); + filter_network(options.filter, NULL, &ln); first = 0; } diff --git a/options.c b/options.c @@ -0,0 +1,48 @@ + +#include "options.h" +#include <stdio.h> +#include <stdlib.h> +#include "commander/commander.h" + +static void opt_channels(command_t *self) { + struct options *options = self->data; + options->channels = self->arg; +}; + +static void opt_nodes(command_t *self) { + struct options *options = self->data; + options->nodes = self->arg; +}; + +static void opt_filter(command_t *self) { + struct options *options = self->data; + options->filter = self->arg; +}; + +static void fail(int code, const char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(code); +} + +void parse_options(int argc, const char *argv[], struct options *options) +{ + command_t cmd; + options->channels = NULL; + options->nodes = NULL; + options->filter = NULL; + + cmd.data = (void*)options; + + command_init(&cmd, argv[0], "0.1"); + cmd.usage = "--channels channels.json --nodes nodes.json [options]"; + + command_option(&cmd, "-c", "--channels <path>", "channels json", opt_channels); + command_option(&cmd, "-n", "--nodes <path>", "nodes json", opt_nodes); + command_option(&cmd, "-f", "--filter <nodeid>", "nodeid to filter on", opt_filter); + + command_parse(&cmd, argc, (char **)argv); + + if (options->channels == NULL || options->nodes == NULL) + command_help(&cmd); +} diff --git a/options.h b/options.h @@ -0,0 +1,15 @@ + +#ifndef LNVIS_OPTIONS_H +#define LNVIS_OPTIONS_H + + +struct options { + const char *channels; + const char *nodes; + const char *filter; +}; + + +void parse_options(int argc, const char *argv[], struct options *options); + +#endif /* LNVIS_OPTIONS_H */