commit dd883c9e4093647b9dc7ac748fa2e9bcf1c5e312
parent 4be8e791a44dcbb0a835fe501351b23d1ca1644b
Author: William Casarin <jb55@jb55.com>
Date: Mon, 20 Aug 2018 20:54:31 -0700
options!
Diffstat:
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 */