/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, RedJack, LLC. * All rights reserved. * * Please see the COPYING file in this distribution for license * details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/cli.h" #include "libcork/core.h" #include "libcork/ds.h" #define streq(a,b) (strcmp((a), (b)) == 0) static struct cork_buffer breadcrumbs_buf = CORK_BUFFER_INIT(); static void cork_command_add_breadcrumb(struct cork_command *command) { cork_buffer_append_printf(&breadcrumbs_buf, " %s", command->name); } #define cork_command_breadcrumbs() ((char *) breadcrumbs_buf.buf) static void cork_command_run(struct cork_command *command, int argc, char **argv); static struct cork_command * cork_command_set_get_subcommand(struct cork_command *command, const char *command_name) { struct cork_command **curr; for (curr = command->set; *curr != NULL; curr++) { if (streq(command_name, (*curr)->name)) { return *curr; } } return NULL; } static void cork_command_set_show_help(struct cork_command *command) { size_t max_length = 0; struct cork_command **curr; /* Calculate the length of the longest command name. */ for (curr = command->set; *curr != NULL; curr++) { size_t len = strlen((*curr)->name); if (len > max_length) { max_length = len; } } /* Then print out the available commands. */ printf("Usage:%s []\n" "\nAvailable commands:\n", cork_command_breadcrumbs()); for (curr = command->set; *curr != NULL; curr++) { printf(" %*s", (int) -max_length, (*curr)->name); if ((*curr)->short_desc != NULL) { printf(" %s\n", (*curr)->short_desc); } else { printf("\n"); } } } static void cork_command_leaf_show_help(struct cork_command *command) { printf("Usage:%s", cork_command_breadcrumbs()); if (command->usage_suffix != NULL) { printf(" %s", command->usage_suffix); } if (command->full_help != NULL) { printf("\n\n%s", command->full_help); } else { printf("\n"); } } void cork_command_show_help(struct cork_command *command, const char *message) { if (message != NULL) { printf("%s\n", message); } if (command->type == CORK_COMMAND_SET) { cork_command_set_show_help(command); } else if (command->type == CORK_LEAF_COMMAND) { cork_command_leaf_show_help(command); } } static void cork_command_set_run_help(struct cork_command *command, int argc, char **argv) { /* When we see the help command when processing a command set, we use any * remaining arguments to identifity which subcommand the user wants help * with. */ /* Skip over the name of the command set */ argc--; argv++; while (argc > 0 && command->type == CORK_COMMAND_SET) { struct cork_command *subcommand = cork_command_set_get_subcommand(command, argv[0]); if (subcommand == NULL) { printf("Unknown command \"%s\".\n" "Usage:%s []\n", argv[0], cork_command_breadcrumbs()); exit(EXIT_FAILURE); } cork_command_add_breadcrumb(subcommand); command = subcommand; argc--; argv++; } cork_command_show_help(command, NULL); } static void cork_command_set_run(struct cork_command *command, int argc, char **argv) { const char *command_name; struct cork_command *subcommand; if (argc == 0) { printf("No command given.\n"); cork_command_set_show_help(command); exit(EXIT_FAILURE); } command_name = argv[0]; /* The "help" command is special. */ if (streq(command_name, "help")) { cork_command_set_run_help(command, argc, argv); return; } /* Otherwise look for a real subcommand with this name. */ subcommand = cork_command_set_get_subcommand(command, command_name); if (subcommand == NULL) { printf("Unknown command \"%s\".\n" "Usage:%s []\n", command_name, cork_command_breadcrumbs()); exit(EXIT_FAILURE); } else { cork_command_run(subcommand, argc, argv); } } static void cork_command_leaf_run(struct cork_command *command, int argc, char **argv) { command->run(argc, argv); } static void cork_command_cleanup(void) { cork_buffer_done(&breadcrumbs_buf); } static void cork_command_run(struct cork_command *command, int argc, char **argv) { cork_command_add_breadcrumb(command); /* If the gives the --help option at this point, describe the current * command. */ if (argc >= 2 && (streq(argv[1], "--help") || streq(argv[1], "-h"))) { cork_command_show_help(command, NULL); return; } /* Otherwise let the command parse any options that occur here. */ if (command->parse_options != NULL) { int option_count = command->parse_options(argc, argv); argc -= option_count; argv += option_count; } else { argc--; argv++; } switch (command->type) { case CORK_COMMAND_SET: cork_command_set_run(command, argc, argv); return; case CORK_LEAF_COMMAND: cork_command_leaf_run(command, argc, argv); return; default: cork_unreachable(); } } int cork_command_main(struct cork_command *root, int argc, char **argv) { /* Clean up after ourselves when the command finishes. */ atexit(cork_command_cleanup); /* Run the root command. */ cork_command_run(root, argc, argv); return EXIT_SUCCESS; }