diff --git a/Makefile b/Makefile index dc59913..731a60f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ test: test.c src/commander.c $(CC) $^ -o $@ -Wall -Wextra + ./test test1 -v --required important + ./test test2 --optional justsoso + ./test test3 ex1 ex2 ex3 clean: rm -f test diff --git a/Readme.md b/Readme.md index a571162..315d7e3 100644 --- a/Readme.md +++ b/Readme.md @@ -18,6 +18,12 @@ $ clib install clibs/commander Usage: example [options] +Commands: + + test1, test1 sub command + test2, test2 sub command + test3, test3 sub command + Options: -V, --version output program version @@ -32,35 +38,71 @@ Options: ```c #include -#include "commander.h" +#include "src/commander.h" + +typedef struct { + int verbose; + const char *required; + const char *optional; +} flags_t; static void verbose(command_t *self) { - printf("verbose: enabled\n"); + flags_t *flag = (flags_t*)(self->data); + flag->verbose = 1; } static void required(command_t *self) { - printf("required: %s\n", self->arg); + flags_t *flag = (flags_t*)(self->data); + flag->required = self->arg; } static void optional(command_t *self) { - printf("optional: %s\n", self->arg); + flags_t *flag = (flags_t*)(self->data); + flag->optional = self->arg; +} + +static void +cmd_test1(command_t *self) { + flags_t *flag = (flags_t*)(self->data); + printf("sub command test1 running! options: verbose '%d' required '%s' optional '%s'\n", flag->verbose, flag->required, flag->optional); +} + +static void +cmd_test2(command_t *self) { + flags_t *flag = (flags_t*)(self->data); + printf("sub command test2 running! options: verbose '%d' required '%s' optional '%s'\n", flag->verbose, flag->required, flag->optional); +} + +static void +cmd_test3(command_t *self) { + printf("sub command test3 running with extra parameters:\n"); + if (self->argc > 1) { + for (int i = 1; i < self->argc; ++i) { + printf("%s\n", self->argv[i]); + } + } + } int -main(int argc, const char **argv){ +main(int argc, char **argv){ + flags_t flags = (flags_t){.verbose = 0, .required = "default", .optional = ""}; command_t cmd; + cmd.data = &flags; command_init(&cmd, argv[0], "0.0.1"); command_option(&cmd, "-v", "--verbose", "enable verbose stuff", verbose); command_option(&cmd, "-r", "--required ", "required arg", required); command_option(&cmd, "-o", "--optional [arg]", "optional arg", optional); + command_sub(&cmd, "test1", "test1 sub command", cmd_test1); + command_sub(&cmd, "test2", "test2 sub command", cmd_test2); + command_sub(&cmd, "test3", "test3 sub command", cmd_test3); + command_parse(&cmd, argc, argv); - printf("additional args:\n"); - for (int i = 0; i < cmd.argc; ++i) { - printf(" - '%s'\n", cmd.argv[i]); - } + command_run(&cmd); + command_free(&cmd); return 0; } diff --git a/src/commander.c b/src/commander.c index 8298d1a..d688728 100644 --- a/src/commander.c +++ b/src/commander.c @@ -40,12 +40,21 @@ void command_help(command_t *self) { printf("\n"); printf(" Usage: %s %s\n", self->name, self->usage); + + printf("\n"); + printf(" Commands:\n"); + printf("\n"); + + for (int i = 0; i < self->command_count; ++i) { + sub_command_t *cmd = &self->commands[i]; + printf(" %s, %s\n", cmd->cmd_name, cmd->description); + } + printf("\n"); printf(" Options:\n"); printf("\n"); - int i; - for (i = 0; i < self->option_count; ++i) { + for (int i = 0; i < self->option_count; ++i) { command_option_t *option = &self->options[i]; printf(" %s, %-25s %s\n" , option->small @@ -68,6 +77,7 @@ command_init(command_t *self, const char *name, const char *version) { self->name = name; self->version = version; self->option_count = self->argc = 0; + self->command_count = 0; self->usage = "[options]"; self->nargv = NULL; command_option(self, "-V", "--version", "output program version", command_version); @@ -190,6 +200,23 @@ command_option(command_t *self, const char *small, const char *large, const char if ('<' == option->argname[0]) option->required_arg = 1; } +/* + * Define a sub command. + */ + +void +command_sub(command_t *self, const char *cmd_name, const char *desc, command_callback_t cb) { + if (self->command_count == COMMANDER_MAX_OPTIONS) { + command_free(self); + error("Maximum sub command definitions exceeded"); + } + int n = self->command_count++; + sub_command_t *cmd = &self->commands[n]; + cmd->cb = cb; + cmd->cmd_name = cmd_name; + cmd->description = desc; + } + /* * Parse `argv` (internal). * Input arguments should be normalized first @@ -267,3 +294,30 @@ command_parse(command_t *self, int argc, char **argv) { command_parse_args(self, argc, self->nargv); self->argv[self->argc] = NULL; } + +/* + * Run command + */ + +void +command_run(command_t *self) { + const char *cli_cmd = self->argv[0]; + if (cli_cmd == NULL) { + printf("At least one sub command required, see help by passing -h\n"); + exit(1); + } + + int pos = -1; + for (int i = 0; i < self->command_count; i++) { + if (strcmp(self->commands[i].cmd_name, cli_cmd) == 0) { + pos = i; + } + } + + if (pos > -1) { + self->commands[pos].cb(self); + } else { + printf("No sub command %s defined.", cli_cmd); + exit(1); + } + } diff --git a/src/commander.h b/src/commander.h index 141fedf..b27c24b 100644 --- a/src/commander.h +++ b/src/commander.h @@ -16,6 +16,14 @@ #define COMMANDER_MAX_OPTIONS 32 #endif +/* + * Max sub commands that can be defined. + */ + +#ifndef COMMANDER_MAX_SUB_COMMANDS +#define COMMANDER_MAX_SUB_COMMANDS 32 +#endif + /* * Max arguments that can be passed. */ @@ -51,6 +59,14 @@ typedef struct { command_callback_t cb; } command_option_t; +/* + * Sub command. + */ +typedef struct { + const char *cmd_name; + const char *description; + command_callback_t cb; +} sub_command_t; /* * Command. */ @@ -63,6 +79,8 @@ typedef struct command { const char *version; int option_count; command_option_t options[COMMANDER_MAX_OPTIONS]; + int command_count; + sub_command_t commands[COMMANDER_MAX_SUB_COMMANDS]; int argc; char *argv[COMMANDER_MAX_ARGS]; char **nargv; @@ -82,7 +100,13 @@ 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_sub(command_t *self, const char *name, const char *desc, command_callback_t cb); + void command_parse(command_t *self, int argc, char **argv); +void +command_run(command_t *self); + #endif /* COMMANDER_H */ diff --git a/test.c b/test.c index e96b576..cb0a1a7 100644 --- a/test.c +++ b/test.c @@ -2,33 +2,69 @@ #include #include "src/commander.h" +typedef struct { + int verbose; + const char *required; + const char *optional; +} flags_t; + static void -verbose() { - printf("verbose: enabled\n"); +verbose(command_t *self) { + flags_t *flag = (flags_t*)(self->data); + flag->verbose = 1; } static void required(command_t *self) { - printf("required: %s\n", self->arg); + flags_t *flag = (flags_t*)(self->data); + flag->required = self->arg; } static void optional(command_t *self) { - printf("optional: %s\n", self->arg); + flags_t *flag = (flags_t*)(self->data); + flag->optional = self->arg; +} + +static void +cmd_test1(command_t *self) { + flags_t *flag = (flags_t*)(self->data); + printf("sub command test1 running! options: verbose '%d' required '%s' optional '%s'\n", flag->verbose, flag->required, flag->optional); +} + +static void +cmd_test2(command_t *self) { + flags_t *flag = (flags_t*)(self->data); + printf("sub command test2 running! options: verbose '%d' required '%s' optional '%s'\n", flag->verbose, flag->required, flag->optional); +} + +static void +cmd_test3(command_t *self) { + printf("sub command test3 running with extra parameters:\n"); + if (self->argc > 1) { + for (int i = 1; i < self->argc; ++i) { + printf("%s\n", self->argv[i]); + } + } + } int main(int argc, char **argv){ + flags_t flags = (flags_t){.verbose = 0, .required = "default", .optional = ""}; command_t cmd; + cmd.data = &flags; command_init(&cmd, argv[0], "0.0.1"); command_option(&cmd, "-v", "--verbose", "enable verbose stuff", verbose); command_option(&cmd, "-r", "--required ", "required arg", required); command_option(&cmd, "-o", "--optional [arg]", "optional arg", optional); + command_sub(&cmd, "test1", "test1 sub command", cmd_test1); + command_sub(&cmd, "test2", "test2 sub command", cmd_test2); + command_sub(&cmd, "test3", "test3 sub command", cmd_test3); + command_parse(&cmd, argc, argv); - printf("additional args:\n"); - for (int i = 0; i < cmd.argc; ++i) { - printf(" - '%s'\n", cmd.argv[i]); - } + command_run(&cmd); + command_free(&cmd); return 0; }