/*
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();
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 = 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 */