/cli/src/cli-rl.c

https://github.com/lkundrak/glusterfs · C · 414 lines · 288 code · 107 blank · 19 comment · 38 complexity · eaf30614ce13b41592fadc2cea3743e5 MD5 · raw file

  1. /*
  2. Copyright (c) 2010-2012 Red Hat, Inc. <http://www.redhat.com>
  3. This file is part of GlusterFS.
  4. This file is licensed to you under your choice of the GNU Lesser
  5. General Public License, version 3 or any later version (LGPLv3 or
  6. later), or the GNU General Public License, version 2 (GPLv2), in all
  7. cases as published by the Free Software Foundation.
  8. */
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include <stdint.h>
  13. #include <pthread.h>
  14. #ifndef _CONFIG_H
  15. #define _CONFIG_H
  16. #include "config.h"
  17. #endif
  18. #include "cli.h"
  19. #include "cli-cmd.h"
  20. #include "cli-mem-types.h"
  21. #include "event.h"
  22. #include <fnmatch.h>
  23. #ifdef HAVE_READLINE
  24. #include <stdio.h>
  25. #include <readline/readline.h>
  26. #include <readline/history.h>
  27. int
  28. cli_rl_out (struct cli_state *state, const char *fmt, va_list ap)
  29. {
  30. int tmp_rl_point = rl_point;
  31. int n = rl_end;
  32. int ret = 0;
  33. if (rl_end >= 0 ) {
  34. rl_kill_text (0, rl_end);
  35. rl_redisplay ();
  36. }
  37. printf ("\r%*s\r", (int)strlen (state->prompt), "");
  38. ret = vprintf (fmt, ap);
  39. printf ("\n");
  40. fflush(stdout);
  41. if (n) {
  42. rl_do_undo ();
  43. rl_point = tmp_rl_point;
  44. rl_reset_line_state ();
  45. }
  46. return ret;
  47. }
  48. int
  49. cli_rl_err (struct cli_state *state, const char *fmt, va_list ap)
  50. {
  51. int tmp_rl_point = rl_point;
  52. int n = rl_end;
  53. int ret = 0;
  54. if (rl_end >= 0 ) {
  55. rl_kill_text (0, rl_end);
  56. rl_redisplay ();
  57. }
  58. fprintf (stderr, "\r%*s\r", (int)strlen (state->prompt), "");
  59. ret = vfprintf (stderr, fmt, ap);
  60. fprintf (stderr, "\n");
  61. fflush(stderr);
  62. if (n) {
  63. rl_do_undo ();
  64. rl_point = tmp_rl_point;
  65. rl_reset_line_state ();
  66. }
  67. return ret;
  68. }
  69. void
  70. cli_rl_process_line (char *line)
  71. {
  72. struct cli_state *state = NULL;
  73. int ret = 0;
  74. state = global_state;
  75. state->rl_processing = 1;
  76. {
  77. ret = cli_cmd_process_line (state, line);
  78. if (ret)
  79. gf_log (THIS->name, GF_LOG_WARNING,
  80. "failed to process line");
  81. add_history (line);
  82. }
  83. state->rl_processing = 0;
  84. }
  85. int
  86. cli_rl_stdin (int fd, int idx, void *data,
  87. int poll_out, int poll_in, int poll_err)
  88. {
  89. rl_callback_read_char ();
  90. return 0;
  91. }
  92. char *
  93. cli_rl_autocomplete_entry (const char *text, int times)
  94. {
  95. struct cli_state *state = NULL;
  96. char *retp = NULL;
  97. state = global_state;
  98. if (!state->matchesp)
  99. return NULL;
  100. retp = *state->matchesp;
  101. state->matchesp++;
  102. return retp ? strdup (retp) : NULL;
  103. }
  104. int
  105. cli_rl_token_count (const char *text)
  106. {
  107. int count = 0;
  108. const char *trav = NULL;
  109. int is_spc = 1;
  110. for (trav = text; *trav; trav++) {
  111. if (*trav == ' ') {
  112. is_spc = 1;
  113. } else {
  114. if (is_spc) {
  115. count++;
  116. is_spc = 0;
  117. }
  118. }
  119. }
  120. if (is_spc)
  121. /* what needs to be autocompleted is a full
  122. new word, and not extend the last word
  123. */
  124. count++;
  125. return count;
  126. }
  127. char **
  128. cli_rl_tokenize (const char *text)
  129. {
  130. int count = 0;
  131. char **tokens = NULL;
  132. char **tokenp = NULL;
  133. char *token = NULL;
  134. char *copy = NULL;
  135. char *saveptr = NULL;
  136. int i = 0;
  137. count = cli_rl_token_count (text);
  138. tokens = calloc (count + 1, sizeof (*tokens));
  139. if (!tokens)
  140. return NULL;
  141. copy = strdup (text);
  142. if (!copy)
  143. goto out;
  144. tokenp = tokens;
  145. for (token = strtok_r (copy, " \t\r\n", &saveptr); token;
  146. token = strtok_r (NULL, " \t\r\n", &saveptr)) {
  147. *tokenp = strdup (token);
  148. if (!*tokenp)
  149. goto out;
  150. tokenp++;
  151. i++;
  152. }
  153. if (i < count) {
  154. /* symbolize that what needs to be autocompleted is
  155. the full set of possible nextwords, and not extend
  156. the last word
  157. */
  158. *tokenp = strdup ("");
  159. if (!*tokenp)
  160. goto out;
  161. tokenp++;
  162. i++;
  163. }
  164. out:
  165. free (copy);
  166. if (i < count) {
  167. cli_cmd_tokens_destroy (tokens);
  168. tokens = NULL;
  169. }
  170. return tokens;
  171. }
  172. char **
  173. cli_rl_get_matches (struct cli_state *state, struct cli_cmd_word *word,
  174. const char *text)
  175. {
  176. char **matches = NULL;
  177. char **matchesp = NULL;
  178. struct cli_cmd_word **next = NULL;
  179. int count = 0;
  180. int len = 0;
  181. len = strlen (text);
  182. if (!word->nextwords)
  183. return NULL;
  184. for (next = word->nextwords; *next; next++)
  185. count++;
  186. matches = calloc (count + 1, sizeof (*matches));
  187. matchesp = matches;
  188. for (next = word->nextwords; *next; next++) {
  189. if ((*next)->match) {
  190. continue;
  191. }
  192. if (strncmp ((*next)->word, text, len) == 0) {
  193. *matchesp = strdup ((*next)->word);
  194. matchesp++;
  195. }
  196. }
  197. return matches;
  198. }
  199. int
  200. cli_rl_autocomplete_prepare (struct cli_state *state, const char *text)
  201. {
  202. struct cli_cmd_word *word = NULL;
  203. struct cli_cmd_word *next = NULL;
  204. char **tokens = NULL;
  205. char **tokenp = NULL;
  206. char *token = NULL;
  207. char **matches = NULL;
  208. tokens = cli_rl_tokenize (text);
  209. if (!tokens)
  210. return 0;
  211. word = &state->tree.root;
  212. for (tokenp = tokens; (token = *tokenp); tokenp++) {
  213. if (!*(tokenp+1)) {
  214. /* last word */
  215. break;
  216. }
  217. next = cli_cmd_nextword (word, token);
  218. word = next;
  219. if (!word)
  220. break;
  221. }
  222. if (!word)
  223. goto out;
  224. matches = cli_rl_get_matches (state, word, token);
  225. state->matches = matches;
  226. state->matchesp = matches;
  227. out:
  228. cli_cmd_tokens_destroy (tokens);
  229. return 0;
  230. }
  231. int
  232. cli_rl_autocomplete_cleanup (struct cli_state *state)
  233. {
  234. if (state->matches)
  235. cli_cmd_tokens_destroy (state->matches);
  236. state->matches = NULL;
  237. state->matchesp = NULL;
  238. return 0;
  239. }
  240. char **
  241. cli_rl_autocomplete (const char *text, int start, int end)
  242. {
  243. struct cli_state *state = NULL;
  244. char **matches = NULL;
  245. char save = 0;
  246. state = global_state;
  247. /* hack to make the autocompletion code neater */
  248. /* fake it as though the cursor is at the end of line */
  249. save = rl_line_buffer[rl_point];
  250. rl_line_buffer[rl_point] = 0;
  251. cli_rl_autocomplete_prepare (state, rl_line_buffer);
  252. matches = rl_completion_matches (text, cli_rl_autocomplete_entry);
  253. cli_rl_autocomplete_cleanup (state);
  254. rl_line_buffer[rl_point] = save;
  255. return matches;
  256. }
  257. static char *
  258. complete_none (const char *txt, int times)
  259. {
  260. return NULL;
  261. }
  262. void *
  263. cli_rl_input (void *_data)
  264. {
  265. struct cli_state *state = NULL;
  266. char *line = NULL;
  267. state = _data;
  268. for (;;) {
  269. line = readline (state->prompt);
  270. if (!line)
  271. exit(0); //break;
  272. cli_rl_process_line (line);
  273. free (line);
  274. }
  275. return NULL;
  276. }
  277. int
  278. cli_rl_enable (struct cli_state *state)
  279. {
  280. int ret = 0;
  281. rl_pre_input_hook = NULL;
  282. rl_attempted_completion_function = cli_rl_autocomplete;
  283. rl_completion_entry_function = complete_none;
  284. if (!state->rl_async) {
  285. ret = pthread_create (&state->input, NULL,
  286. cli_rl_input, state);
  287. if (ret == 0)
  288. state->rl_enabled = 1;
  289. goto out;
  290. }
  291. ret = event_register (state->ctx->event_pool, 0, cli_rl_stdin, state,
  292. 1, 0);
  293. if (ret == -1)
  294. goto out;
  295. state->rl_enabled = 1;
  296. rl_callback_handler_install (state->prompt, cli_rl_process_line);
  297. out:
  298. return state->rl_enabled;
  299. }
  300. #else /* HAVE_READLINE */
  301. int
  302. cli_rl_enable (struct cli_state *state)
  303. {
  304. return 0;
  305. }
  306. #endif /* HAVE_READLINE */