summaryrefslogtreecommitdiffstats
path: root/cli/src/cli-rl.c
diff options
context:
space:
mode:
Diffstat (limited to 'cli/src/cli-rl.c')
-rw-r--r--cli/src/cli-rl.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/cli/src/cli-rl.c b/cli/src/cli-rl.c
new file mode 100644
index 00000000000..7a38a0b882a
--- /dev/null
+++ b/cli/src/cli-rl.c
@@ -0,0 +1,402 @@
+/*
+ Copyright (c) 2010-2012 Red Hat, Inc. <http://www.redhat.com>
+ This file is part of GlusterFS.
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#include "cli.h"
+#include "cli-cmd.h"
+#include "cli-mem-types.h"
+
+#include <glusterfs/gf-event.h>
+
+#include <fnmatch.h>
+
+#ifdef HAVE_READLINE
+
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+int
+cli_rl_out(struct cli_state *state, const char *fmt, va_list ap)
+{
+ int tmp_rl_point = rl_point;
+ int n = rl_end;
+ int ret = 0;
+
+ if (rl_end >= 0) {
+ rl_kill_text(0, rl_end);
+ rl_redisplay();
+ }
+
+ printf("\r%*s\r", (int)strlen(state->prompt), "");
+
+ ret = vprintf(fmt, ap);
+
+ printf("\n");
+ fflush(stdout);
+
+ if (n) {
+ rl_do_undo();
+ rl_point = tmp_rl_point;
+ rl_reset_line_state();
+ }
+
+ return ret;
+}
+
+int
+cli_rl_err(struct cli_state *state, const char *fmt, va_list ap)
+{
+ int tmp_rl_point = rl_point;
+ int n = rl_end;
+ int ret = 0;
+
+ if (rl_end >= 0) {
+ rl_kill_text(0, rl_end);
+ rl_redisplay();
+ }
+
+ fprintf(stderr, "\r%*s\r", (int)strlen(state->prompt), "");
+
+ ret = vfprintf(stderr, fmt, ap);
+
+ fprintf(stderr, "\n");
+ fflush(stderr);
+
+ if (n) {
+ rl_do_undo();
+ rl_point = tmp_rl_point;
+ rl_reset_line_state();
+ }
+
+ return ret;
+}
+
+void
+cli_rl_process_line(char *line)
+{
+ struct cli_state *state = NULL;
+ int ret = 0;
+
+ state = global_state;
+
+ state->rl_processing = 1;
+ {
+ ret = cli_cmd_process_line(state, line);
+ if (ret)
+ gf_log(THIS->name, GF_LOG_WARNING, "failed to process line");
+
+ add_history(line);
+ }
+ state->rl_processing = 0;
+}
+
+void
+cli_rl_stdin(int fd, int idx, int gen, void *data, int poll_out, int poll_in,
+ int poll_err, char event_thread_died)
+{
+ struct cli_state *state = NULL;
+
+ state = data;
+
+ rl_callback_read_char();
+
+ gf_event_handled(state->ctx->event_pool, fd, idx, gen);
+
+ return;
+}
+
+char *
+cli_rl_autocomplete_entry(const char *text, int times)
+{
+ struct cli_state *state = NULL;
+ char *retp = NULL;
+
+ state = global_state;
+
+ if (!state->matchesp)
+ return NULL;
+
+ retp = *state->matchesp;
+
+ state->matchesp++;
+
+ return retp ? strdup(retp) : NULL;
+}
+
+int
+cli_rl_token_count(const char *text)
+{
+ int count = 0;
+ const char *trav = NULL;
+ int is_spc = 1;
+
+ for (trav = text; *trav; trav++) {
+ if (*trav == ' ') {
+ is_spc = 1;
+ } else {
+ if (is_spc) {
+ count++;
+ is_spc = 0;
+ }
+ }
+ }
+
+ if (is_spc)
+ /* what needs to be autocompleted is a full
+ new word, and not extend the last word
+ */
+ count++;
+
+ return count;
+}
+
+char **
+cli_rl_tokenize(const char *text)
+{
+ int count = 0;
+ char **tokens = NULL;
+ char **tokenp = NULL;
+ char *token = NULL;
+ char *copy = NULL;
+ char *saveptr = NULL;
+ int i = 0;
+
+ count = cli_rl_token_count(text);
+
+ tokens = calloc(count + 1, sizeof(*tokens));
+ if (!tokens)
+ return NULL;
+
+ copy = strdup(text);
+ if (!copy)
+ goto out;
+
+ tokenp = tokens;
+
+ for (token = strtok_r(copy, " \t\r\n", &saveptr); token;
+ token = strtok_r(NULL, " \t\r\n", &saveptr)) {
+ *tokenp = strdup(token);
+
+ if (!*tokenp)
+ goto out;
+ tokenp++;
+ i++;
+ }
+
+ if (i < count) {
+ /* symbolize that what needs to be autocompleted is
+ the full set of possible nextwords, and not extend
+ the last word
+ */
+ *tokenp = strdup("");
+ if (!*tokenp)
+ goto out;
+ tokenp++;
+ i++;
+ }
+
+out:
+ free(copy);
+
+ if (i < count) {
+ cli_cmd_tokens_destroy(tokens);
+ tokens = NULL;
+ }
+
+ return tokens;
+}
+
+char **
+cli_rl_get_matches(struct cli_state *state, struct cli_cmd_word *word,
+ const char *text)
+{
+ char **matches = NULL;
+ char **matchesp = NULL;
+ struct cli_cmd_word **next = NULL;
+ int count = 0;
+ int len = 0;
+
+ len = strlen(text);
+
+ if (!word->nextwords)
+ return NULL;
+
+ for (next = word->nextwords; *next; next++)
+ count++;
+
+ matches = calloc(count + 1, sizeof(*matches));
+ matchesp = matches;
+
+ for (next = word->nextwords; *next; next++) {
+ if ((*next)->match) {
+ continue;
+ }
+
+ if (strncmp((*next)->word, text, len) == 0) {
+ *matchesp = strdup((*next)->word);
+ matchesp++;
+ }
+ }
+
+ return matches;
+}
+
+int
+cli_rl_autocomplete_prepare(struct cli_state *state, const char *text)
+{
+ struct cli_cmd_word *word = NULL;
+ struct cli_cmd_word *next = NULL;
+ char **tokens = NULL;
+ char **tokenp = NULL;
+ char *token = NULL;
+ char **matches = NULL;
+
+ tokens = cli_rl_tokenize(text);
+ if (!tokens)
+ return 0;
+
+ word = &state->tree.root;
+
+ for (tokenp = tokens; (token = *tokenp); tokenp++) {
+ if (!*(tokenp + 1)) {
+ /* last word */
+ break;
+ }
+
+ next = cli_cmd_nextword(word, token);
+ word = next;
+ if (!word)
+ break;
+ }
+
+ if (!word || !token)
+ goto out;
+
+ matches = cli_rl_get_matches(state, word, token);
+
+ state->matches = matches;
+ state->matchesp = matches;
+
+out:
+ cli_cmd_tokens_destroy(tokens);
+ return 0;
+}
+
+int
+cli_rl_autocomplete_cleanup(struct cli_state *state)
+{
+ if (state->matches)
+ cli_cmd_tokens_destroy(state->matches);
+
+ state->matches = NULL;
+ state->matchesp = NULL;
+
+ return 0;
+}
+
+char **
+cli_rl_autocomplete(const char *text, int start, int end)
+{
+ struct cli_state *state = NULL;
+ char **matches = NULL;
+ char save = 0;
+
+ state = global_state;
+
+ /* hack to make the autocompletion code neater */
+ /* fake it as though the cursor is at the end of line */
+
+ save = rl_line_buffer[rl_point];
+ rl_line_buffer[rl_point] = 0;
+
+ cli_rl_autocomplete_prepare(state, rl_line_buffer);
+
+ matches = rl_completion_matches(text, cli_rl_autocomplete_entry);
+
+ cli_rl_autocomplete_cleanup(state);
+
+ rl_line_buffer[rl_point] = save;
+
+ return matches;
+}
+
+static char *
+complete_none(const char *txt, int times)
+{
+ return NULL;
+}
+
+void *
+cli_rl_input(void *_data)
+{
+ struct cli_state *state = NULL;
+ char *line = NULL;
+
+ state = _data;
+
+ fprintf(stderr,
+ "Welcome to gluster prompt, type 'help' to see the available "
+ "commands.\n");
+ for (;;) {
+ line = readline(state->prompt);
+ if (!line)
+ exit(0); // break;
+
+ if (*line)
+ cli_rl_process_line(line);
+
+ free(line);
+ }
+
+ return NULL;
+}
+
+int
+cli_rl_enable(struct cli_state *state)
+{
+ int ret = 0;
+
+ rl_pre_input_hook = NULL;
+ rl_attempted_completion_function = cli_rl_autocomplete;
+ rl_completion_entry_function = complete_none;
+
+ if (!state->rl_async) {
+ ret = pthread_create(&state->input, NULL, cli_rl_input, state);
+ if (ret == 0)
+ state->rl_enabled = 1;
+ goto out;
+ }
+
+ ret = gf_event_register(state->ctx->event_pool, 0, cli_rl_stdin, state, 1,
+ 0, 0);
+ if (ret == -1)
+ goto out;
+
+ state->rl_enabled = 1;
+ rl_callback_handler_install(state->prompt, cli_rl_process_line);
+
+out:
+ return state->rl_enabled;
+}
+
+#else /* HAVE_READLINE */
+
+int
+cli_rl_enable(struct cli_state *state)
+{
+ return 0;
+}
+
+#endif /* HAVE_READLINE */