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

/* -*- 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;
}