shell_help.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. * shell_help.c
  3. */
  4. #include "private.h"
  5. #include "clish/types.h"
  6. #include "lub/string.h"
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10. /*--------------------------------------------------------- */
  11. /*
  12. * Provide a detailed list of the possible command completions
  13. */
  14. static void available_commands(clish_shell_t *this,
  15. clish_help_t *help, const char *line, size_t *max_width)
  16. {
  17. const clish_command_t *cmd;
  18. clish_shell_iterator_t iter;
  19. if (max_width)
  20. *max_width = 0;
  21. /* Search for COMMAND completions */
  22. clish_shell_iterator_init(&iter, CLISH_NSPACE_HELP);
  23. while ((cmd = clish_shell_find_next_completion(this, line, &iter))) {
  24. size_t width;
  25. const char *name = clish_command__get_suffix(cmd);
  26. if (max_width) {
  27. width = strlen(name);
  28. if (width > *max_width)
  29. *max_width = width;
  30. }
  31. lub_argv_add(help->name, name);
  32. lub_argv_add(help->help, clish_command__get_text(cmd));
  33. lub_argv_add(help->detail, clish_command__get_detail(cmd));
  34. }
  35. }
  36. /*--------------------------------------------------------- */
  37. static int available_params(clish_shell_t *this,
  38. clish_help_t *help, const clish_command_t *cmd,
  39. const char *line, size_t *max_width)
  40. {
  41. unsigned int index = lub_string_wordcount(line);
  42. unsigned int idx = lub_string_wordcount(clish_command__get_name(cmd));
  43. lub_argv_t *argv = NULL;
  44. clish_pargv_t *completion = NULL;
  45. clish_pargv_t *pargv = NULL;
  46. unsigned int i = 0;
  47. unsigned int cnt = 0;
  48. clish_pargv_status_e status = CLISH_LINE_OK;
  49. clish_context_t context = {};
  50. /* Empty line */
  51. if (0 == index)
  52. return -1;
  53. if (line[strlen(line) - 1] != ' ')
  54. index--;
  55. argv = lub_argv_new(line, 0);
  56. /* get the parameter definition */
  57. completion = clish_pargv_new();
  58. pargv = clish_pargv_new();
  59. /* Prepare context */
  60. clish_context_init(&context, this);
  61. clish_context__set_cmd(&context, cmd);
  62. clish_context__set_pargv(&context, pargv);
  63. status = clish_shell_parse_pargv(pargv, cmd, &context,
  64. clish_command__get_paramv(cmd),
  65. argv, &idx, completion, index);
  66. clish_pargv_delete(pargv);
  67. cnt = clish_pargv__get_count(completion);
  68. /* Calculate the longest name */
  69. for (i = 0; i < cnt; i++) {
  70. const clish_param_t *param = NULL;
  71. const char *name = NULL;
  72. unsigned clen = 0;
  73. param = clish_pargv__get_param(completion, i);
  74. if (CLISH_PARAM_SUBCOMMAND == clish_param__get_mode(param))
  75. name = clish_param__get_value(param);
  76. else
  77. name = clish_ptype__get_text(clish_param__get_ptype(param));
  78. if (name)
  79. clen = strlen(name);
  80. if (max_width && (clen > *max_width))
  81. *max_width = clen;
  82. clish_param_help(param, help);
  83. }
  84. clish_pargv_delete(completion);
  85. lub_argv_delete(argv);
  86. /* It's a completed command */
  87. if (CLISH_LINE_OK == status)
  88. return 0;
  89. /* Incompleted command */
  90. return -1;
  91. }
  92. /*--------------------------------------------------------- */
  93. void clish_shell_help(clish_shell_t *this, const char *line)
  94. {
  95. clish_help_t help;
  96. size_t max_width = 0;
  97. const clish_command_t *cmd;
  98. unsigned int i;
  99. help.name = lub_argv_new(NULL, 0);
  100. help.help = lub_argv_new(NULL, 0);
  101. help.detail = lub_argv_new(NULL, 0);
  102. /* Get COMMAND completions */
  103. available_commands(this, &help, line, &max_width);
  104. /* Resolve a command */
  105. cmd = clish_shell_resolve_command(this, line);
  106. /* Search for PARAM completion */
  107. if (cmd) {
  108. size_t width = 0;
  109. int status;
  110. status = available_params(this, &help, cmd, line, &width);
  111. if (width > max_width)
  112. max_width = width;
  113. /* Add <cr> if command is completed */
  114. if (!status) {
  115. lub_argv_add(help.name, "<cr>");
  116. lub_argv_add(help.help, NULL);
  117. lub_argv_add(help.detail, NULL);
  118. }
  119. }
  120. if (lub_argv__get_count(help.name) == 0)
  121. goto end;
  122. /* Print help messages */
  123. for (i = 0; i < lub_argv__get_count(help.name); i++) {
  124. fprintf(stderr, " %-*s %s\n", (int)max_width,
  125. lub_argv__get_arg(help.name, i),
  126. lub_argv__get_arg(help.help, i) ?
  127. lub_argv__get_arg(help.help, i) : "");
  128. }
  129. /* Print details */
  130. if ((lub_argv__get_count(help.name) == 1) &&
  131. (SHELL_STATE_HELPING == this->state)) {
  132. const char *detail = lub_argv__get_arg(help.detail, 0);
  133. if (detail)
  134. fprintf(stderr, "%s\n", detail);
  135. }
  136. /* update the state */
  137. if (this->state == SHELL_STATE_HELPING)
  138. this->state = SHELL_STATE_OK;
  139. else
  140. this->state = SHELL_STATE_HELPING;
  141. end:
  142. lub_argv_delete(help.name);
  143. lub_argv_delete(help.help);
  144. lub_argv_delete(help.detail);
  145. }
  146. /*--------------------------------------------------------- */