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

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2012, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license
  7. * details.
  8. * ----------------------------------------------------------------------
  9. */
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "libcork/cli.h"
  14. #include "libcork/core.h"
  15. #include "libcork/ds.h"
  16. #define streq(a,b) (strcmp((a), (b)) == 0)
  17. static struct cork_buffer breadcrumbs_buf = CORK_BUFFER_INIT();
  18. static void
  19. cork_command_add_breadcrumb(struct cork_command *command)
  20. {
  21. cork_buffer_append_printf(&breadcrumbs_buf, " %s", command->name);
  22. }
  23. #define cork_command_breadcrumbs() ((char *) breadcrumbs_buf.buf)
  24. static void
  25. cork_command_run(struct cork_command *command, int argc, char **argv);
  26. static struct cork_command *
  27. cork_command_set_get_subcommand(struct cork_command *command,
  28. const char *command_name)
  29. {
  30. struct cork_command **curr;
  31. for (curr = command->set; *curr != NULL; curr++) {
  32. if (streq(command_name, (*curr)->name)) {
  33. return *curr;
  34. }
  35. }
  36. return NULL;
  37. }
  38. static void
  39. cork_command_set_show_help(struct cork_command *command)
  40. {
  41. size_t max_length = 0;
  42. struct cork_command **curr;
  43. /* Calculate the length of the longest command name. */
  44. for (curr = command->set; *curr != NULL; curr++) {
  45. size_t len = strlen((*curr)->name);
  46. if (len > max_length) {
  47. max_length = len;
  48. }
  49. }
  50. /* Then print out the available commands. */
  51. printf("Usage:%s <command> [<options>]\n"
  52. "\nAvailable commands:\n",
  53. cork_command_breadcrumbs());
  54. for (curr = command->set; *curr != NULL; curr++) {
  55. printf(" %*s", (int) -max_length, (*curr)->name);
  56. if ((*curr)->short_desc != NULL) {
  57. printf(" %s\n", (*curr)->short_desc);
  58. } else {
  59. printf("\n");
  60. }
  61. }
  62. }
  63. static void
  64. cork_command_leaf_show_help(struct cork_command *command)
  65. {
  66. printf("Usage:%s", cork_command_breadcrumbs());
  67. if (command->usage_suffix != NULL) {
  68. printf(" %s", command->usage_suffix);
  69. }
  70. if (command->full_help != NULL) {
  71. printf("\n\n%s", command->full_help);
  72. } else {
  73. printf("\n");
  74. }
  75. }
  76. void
  77. cork_command_show_help(struct cork_command *command, const char *message)
  78. {
  79. if (message != NULL) {
  80. printf("%s\n", message);
  81. }
  82. if (command->type == CORK_COMMAND_SET) {
  83. cork_command_set_show_help(command);
  84. } else if (command->type == CORK_LEAF_COMMAND) {
  85. cork_command_leaf_show_help(command);
  86. }
  87. }
  88. static void
  89. cork_command_set_run_help(struct cork_command *command, int argc, char **argv)
  90. {
  91. /* When we see the help command when processing a command set, we use any
  92. * remaining arguments to identifity which subcommand the user wants help
  93. * with. */
  94. /* Skip over the name of the command set */
  95. argc--;
  96. argv++;
  97. while (argc > 0 && command->type == CORK_COMMAND_SET) {
  98. struct cork_command *subcommand =
  99. cork_command_set_get_subcommand(command, argv[0]);
  100. if (subcommand == NULL) {
  101. printf("Unknown command \"%s\".\n"
  102. "Usage:%s <command> [<options>]\n",
  103. argv[0], cork_command_breadcrumbs());
  104. exit(EXIT_FAILURE);
  105. }
  106. cork_command_add_breadcrumb(subcommand);
  107. command = subcommand;
  108. argc--;
  109. argv++;
  110. }
  111. cork_command_show_help(command, NULL);
  112. }
  113. static void
  114. cork_command_set_run(struct cork_command *command, int argc, char **argv)
  115. {
  116. const char *command_name;
  117. struct cork_command *subcommand;
  118. if (argc == 0) {
  119. printf("No command given.\n");
  120. cork_command_set_show_help(command);
  121. exit(EXIT_FAILURE);
  122. }
  123. command_name = argv[0];
  124. /* The "help" command is special. */
  125. if (streq(command_name, "help")) {
  126. cork_command_set_run_help(command, argc, argv);
  127. return;
  128. }
  129. /* Otherwise look for a real subcommand with this name. */
  130. subcommand = cork_command_set_get_subcommand(command, command_name);
  131. if (subcommand == NULL) {
  132. printf("Unknown command \"%s\".\n"
  133. "Usage:%s <command> [<options>]\n",
  134. command_name, cork_command_breadcrumbs());
  135. exit(EXIT_FAILURE);
  136. } else {
  137. cork_command_run(subcommand, argc, argv);
  138. }
  139. }
  140. static void
  141. cork_command_leaf_run(struct cork_command *command, int argc, char **argv)
  142. {
  143. command->run(argc, argv);
  144. }
  145. static void
  146. cork_command_cleanup(void)
  147. {
  148. cork_buffer_done(&breadcrumbs_buf);
  149. }
  150. static void
  151. cork_command_run(struct cork_command *command, int argc, char **argv)
  152. {
  153. cork_command_add_breadcrumb(command);
  154. /* If the gives the --help option at this point, describe the current
  155. * command. */
  156. if (argc >= 2 && (streq(argv[1], "--help") || streq(argv[1], "-h"))) {
  157. cork_command_show_help(command, NULL);
  158. return;
  159. }
  160. /* Otherwise let the command parse any options that occur here. */
  161. if (command->parse_options != NULL) {
  162. int option_count = command->parse_options(argc, argv);
  163. argc -= option_count;
  164. argv += option_count;
  165. } else {
  166. argc--;
  167. argv++;
  168. }
  169. switch (command->type) {
  170. case CORK_COMMAND_SET:
  171. cork_command_set_run(command, argc, argv);
  172. return;
  173. case CORK_LEAF_COMMAND:
  174. cork_command_leaf_run(command, argc, argv);
  175. return;
  176. default:
  177. cork_unreachable();
  178. }
  179. }
  180. int
  181. cork_command_main(struct cork_command *root, int argc, char **argv)
  182. {
  183. /* Clean up after ourselves when the command finishes. */
  184. atexit(cork_command_cleanup);
  185. /* Run the root command. */
  186. cork_command_run(root, argc, argv);
  187. return EXIT_SUCCESS;
  188. }