cli: Add a new CLI wrapper
This new CLI wrapper introduces a command mode, like we are used from
Git for instance. The first argument specifies the command mode, which
is `memory` for the classic flashprog CLI. Alternatively to a first
argument, it can be called as `flashprog-cmd`. This will allow us to
add more CLI features that can be developed independently from the
classic CLI.
For instance, flashprog could then be called like this:
$ flashprog memory -p ch341a_spi
$ flashprog mem -p ch341a_spi
If no command mode is specified, we fall back to the classic CLI for
convenience. So this is also still possible:
$ flashprog -p ch341a_spi
Change-Id: I98cb110b47ebce52daf2e0972fc4565ef9d40242
Signed-off-by: Nico Huber <nico.h@gmx.de>
diff --git a/cli.c b/cli.c
new file mode 100644
index 0000000..82723fd
--- /dev/null
+++ b/cli.c
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the flashprog project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "flash.h"
+#include "cli.h"
+
+static const char *const command_prefix = "flashprog-";
+
+static const struct {
+ const char *name;
+ int (*main)(int argc, char *argv[]);
+} commands[] = {
+ { "mem", flashprog_classic_main },
+ { "memory", flashprog_classic_main },
+};
+
+static void usage(const char *const name)
+{
+ fprintf(stderr, "\nUsage: %s [<command>] [<argument>...]\n", name);
+ fprintf(stderr, "\nWhere <command> can be\n\n"
+ " mem[ory] Standard memory operations\n"
+ " (read/erase/write/verify)\n"
+ "\n"
+ "The default is 'memory'. See `%s <command> --help`\n"
+ "for further instructions.\n\n", name);
+ exit(1);
+}
+
+static int combine_argv01(char *argv[])
+{
+ const size_t len = strlen(argv[0]) + 1 + strlen(argv[1]) + 1;
+ char *const argv0 = malloc(len);
+ if (!argv0) {
+ fprintf(stderr, "Out of memory!\n");
+ return 1;
+ }
+ snprintf(argv0, len, "%s %s", argv[0], argv[1]);
+ argv[1] = argv0;
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *cmd;
+ size_t i;
+
+ print_version();
+ print_banner();
+
+ if (argc < 1)
+ usage("flashprog");
+
+ /* Turn something like `./flashprog-cmd` into `flashprog-cmd`: */
+ const char *const slash = strrchr(argv[0], '/');
+ if (slash)
+ cmd = slash + 1;
+ else
+ cmd = argv[0];
+
+ /* Turn `flashprog-cmd` into `cmd`: */
+ if (!strncmp(cmd, command_prefix, strlen(command_prefix)))
+ cmd += strlen(command_prefix);
+
+ /* Run `cmd` if found: */
+ for (i = 0; i < ARRAY_SIZE(commands); ++i) {
+ if (!strcmp(cmd, commands[i].name))
+ return commands[i].main(argc, argv);
+ }
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ /* Try to find command as first argument in argv[1]: */
+ for (i = 0; i < ARRAY_SIZE(commands); ++i) {
+ if (!strcmp(argv[1], commands[i].name)) {
+ /* Squash argv[0] into argv[1]: */
+ if (combine_argv01(argv))
+ return 1;
+ return commands[i].main(argc - 1, argv + 1);
+ }
+ }
+
+ /* We're still here? Fall back to classic cli: */
+ return flashprog_classic_main(argc, argv);
+}