shell_help.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*
  2. * shell_help.c
  3. */
  4. #include "private.h"
  5. #include "lub/string.h"
  6. #include <stdio.h>
  7. #include <string.h>
  8. /*--------------------------------------------------------- */
  9. /*
  10. * Provide a detailed list of the possible command completions
  11. */
  12. static void
  13. available_commands(clish_shell_t * this, const char *line, bool_t full)
  14. {
  15. char *buf = NULL;
  16. size_t max_width = 0;
  17. const clish_command_t *cmd;
  18. if (NULL == clish_shell_getfirst_command(this, line, CLISH_NSPACE_HELP)) {
  19. /*
  20. * A totally wrong command has been inputed
  21. * Indicate the point of error and display
  22. * a list of possible commands
  23. */
  24. char *prompt = clish_view__get_prompt(this->view,
  25. this->viewid);
  26. unsigned error_offset = strlen(prompt) + 1;
  27. lub_string_free(prompt);
  28. /* find the best match... */
  29. cmd = clish_shell_resolve_prefix(this, line);
  30. if (NULL != cmd) {
  31. error_offset +=
  32. strlen(clish_command__get_name(cmd)) + 1;
  33. /* take a copy for help purposes */
  34. buf = lub_string_dup(clish_command__get_name(cmd));
  35. } else {
  36. /* show all possible commands */
  37. buf = lub_string_dup("");
  38. }
  39. /* indicate the point of error */
  40. fprintf(stderr, "%*s\n", error_offset, "^");
  41. } else {
  42. /* take a copy */
  43. buf = lub_string_dup(line);
  44. }
  45. /* iterate round to determine max_width */
  46. for (cmd = clish_shell_getfirst_command(this, buf, CLISH_NSPACE_HELP);
  47. cmd; cmd = clish_shell_getnext_command(this, buf)) {
  48. size_t width;
  49. const char *name;
  50. if (full) {
  51. name = clish_command__get_name(cmd);
  52. } else {
  53. name = clish_command__get_suffix(cmd);
  54. }
  55. width = strlen(name);
  56. if (width > max_width) {
  57. max_width = width;
  58. }
  59. }
  60. /* now iterate round to print the help */
  61. for (cmd = clish_shell_getfirst_command(this, buf, CLISH_NSPACE_HELP);
  62. cmd; cmd = clish_shell_getnext_command(this, buf)) {
  63. const char *name;
  64. if (full) {
  65. name = clish_command__get_name(cmd);
  66. } else {
  67. name = clish_command__get_suffix(cmd);
  68. }
  69. fprintf(stderr, "%-*s %s\n",
  70. (int)max_width, name, clish_command__get_text(cmd));
  71. }
  72. /* cleanup */
  73. lub_string_free(buf);
  74. }
  75. /*--------------------------------------------------------- */
  76. void clish_shell_help(clish_shell_t * this, const char *line)
  77. {
  78. const clish_command_t *cmd, *next_cmd, *first_cmd;
  79. /* if there are further commands then we need to show them too */
  80. cmd = clish_shell_resolve_prefix(this, line);
  81. if (NULL != cmd) {
  82. clish_shell_iterator_t iter;
  83. /* skip the command already known about */
  84. clish_shell_iterator_init(&iter, CLISH_NSPACE_HELP);
  85. first_cmd = clish_shell_find_next_completion(this, line, &iter);
  86. next_cmd = clish_shell_find_next_completion(this, line, &iter);
  87. clish_shell_iterator_fini(&iter);
  88. } else {
  89. first_cmd = next_cmd = NULL;
  90. }
  91. if ((NULL != cmd) && (NULL == next_cmd)
  92. && (!first_cmd || (first_cmd == cmd))) {
  93. /* we've resolved a particular command */
  94. switch (this->state) {
  95. case SHELL_STATE_HELPING:
  96. {
  97. const char *detail =
  98. clish_command__get_detail(cmd);
  99. if (NULL != detail) {
  100. fprintf(stderr, "%s\n", detail);
  101. } else {
  102. /* get the command to describe itself */
  103. clish_command_help(cmd, line);
  104. }
  105. break;
  106. }
  107. case SHELL_STATE_READY:
  108. case SHELL_STATE_SCRIPT_ERROR:
  109. /* get the command to provide help */
  110. clish_command_help(cmd, line);
  111. break;
  112. case SHELL_STATE_INITIALISING:
  113. case SHELL_STATE_CLOSING:
  114. /* do nothing */
  115. break;
  116. }
  117. } else {
  118. /* dump the available commands */
  119. available_commands(this, line, BOOL_FALSE);
  120. }
  121. /* update the state */
  122. if (this->state == SHELL_STATE_HELPING) {
  123. this->state = SHELL_STATE_READY;
  124. } else {
  125. this->state = SHELL_STATE_HELPING;
  126. }
  127. }
  128. /*--------------------------------------------------------- */