/* Copyright (c) 2010-2012 Red Hat, Inc. 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 #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" #include #include #ifdef HAVE_READLINE #include #include #include 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 */