You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
5.8 KiB
225 lines
5.8 KiB
/* -*- coding: utf-8 -*-
|
|
* ----------------------------------------------------------------------
|
|
* Copyright © 2012, RedJack, LLC.
|
|
* All rights reserved.
|
|
*
|
|
* Please see the COPYING file in this distribution for license
|
|
* details.
|
|
* ----------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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 <command> [<options>]\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 <command> [<options>]\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 <command> [<options>]\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;
|
|
}
|