| /* |
| * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
| * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. |
| * |
| * This file is part of LVM2. |
| * |
| * This copyrighted material is made available to anyone wishing to use, |
| * modify, copy, or redistribute it subject to the terms and conditions |
| * of the GNU General Public License v.2. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "tools.h" |
| |
| #include "lvm2cmdline.h" |
| |
| int main(int argc, char **argv) |
| { |
| return lvm2_main(argc, argv); |
| } |
| |
| #ifdef READLINE_SUPPORT |
| |
| # include <readline/readline.h> |
| # include <readline/history.h> |
| # ifndef HAVE_RL_COMPLETION_MATCHES |
| # define rl_completion_matches(a, b) completion_matches((char *)a, b) |
| # define rl_completion_func_t CPPFunction |
| # endif |
| |
| static struct cmdline_context *_cmdline; |
| |
| /* List matching commands */ |
| static char *_list_cmds(const char *text, int state) |
| { |
| static int i = 0; |
| static size_t len = 0; |
| |
| /* Initialise if this is a new completion attempt */ |
| if (!state) { |
| i = 0; |
| len = strlen(text); |
| } |
| |
| while (i < _cmdline->num_commands) |
| if (!strncmp(text, _cmdline->commands[i++].name, len)) |
| return strdup(_cmdline->commands[i - 1].name); |
| |
| return NULL; |
| } |
| |
| /* List matching arguments */ |
| static char *_list_args(const char *text, int state) |
| { |
| static int match_no = 0; |
| static size_t len = 0; |
| static struct command *com; |
| |
| /* Initialise if this is a new completion attempt */ |
| if (!state) { |
| char *s = rl_line_buffer; |
| int j; |
| |
| match_no = 0; |
| com = NULL; |
| len = strlen(text); |
| |
| /* Find start of first word in line buffer */ |
| while (isspace(*s)) |
| s++; |
| |
| /* Look for word in list of commands */ |
| for (j = 0; j < _cmdline->num_commands; j++) { |
| const char *p; |
| char *q = s; |
| |
| p = _cmdline->commands[j].name; |
| while (*p == *q) { |
| p++; |
| q++; |
| } |
| if ((!*p) && *q == ' ') { |
| com = _cmdline->commands + j; |
| break; |
| } |
| } |
| } |
| |
| if (!com) |
| return NULL; |
| |
| /* Short form arguments */ |
| if (len < 3) { |
| while (match_no < com->num_args) { |
| char s[3]; |
| char c; |
| if (!(c = (_cmdline->arg_props + |
| com->valid_args[match_no++])->short_arg)) |
| continue; |
| |
| sprintf(s, "-%c", c); |
| if (!strncmp(text, s, len)) |
| return strdup(s); |
| } |
| } |
| |
| /* Long form arguments */ |
| if (match_no < com->num_args) |
| match_no = com->num_args; |
| |
| while (match_no - com->num_args < com->num_args) { |
| const char *l; |
| l = (_cmdline->arg_props + |
| com->valid_args[match_no++ - com->num_args])->long_arg; |
| if (*(l + 2) && !strncmp(text, l, len)) |
| return strdup(l); |
| } |
| |
| return NULL; |
| } |
| |
| /* Custom completion function */ |
| static char **_completion(const char *text, int start_pos, |
| int end_pos __attribute__((unused))) |
| { |
| char **match_list = NULL; |
| int p = 0; |
| |
| while (isspace((int) *(rl_line_buffer + p))) |
| p++; |
| |
| /* First word should be one of our commands */ |
| if (start_pos == p) |
| match_list = rl_completion_matches(text, _list_cmds); |
| |
| else if (*text == '-') |
| match_list = rl_completion_matches(text, _list_args); |
| /* else other args */ |
| |
| /* No further completion */ |
| rl_attempted_completion_over = 1; |
| return match_list; |
| } |
| |
| static int _hist_file(char *buffer, size_t size) |
| { |
| char *e = getenv("HOME"); |
| |
| if (dm_snprintf(buffer, size, "%s/.lvm_history", e) < 0) { |
| log_error("$HOME/.lvm_history: path too long"); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static void _read_history(struct cmd_context *cmd) |
| { |
| char hist_file[PATH_MAX]; |
| |
| if (!_hist_file(hist_file, sizeof(hist_file))) |
| return; |
| |
| if (read_history(hist_file)) |
| log_very_verbose("Couldn't read history from %s.", hist_file); |
| |
| stifle_history(find_config_tree_int(cmd, shell_history_size_CFG, NULL)); |
| } |
| |
| static void _write_history(void) |
| { |
| char hist_file[PATH_MAX]; |
| |
| if (!_hist_file(hist_file, sizeof(hist_file))) |
| return; |
| |
| if (write_history(hist_file)) |
| log_very_verbose("Couldn't write history to %s.", hist_file); |
| } |
| |
| int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline) |
| { |
| int argc, ret; |
| char *input = NULL, *args[MAX_ARGS], **argv; |
| |
| rl_readline_name = "lvm"; |
| rl_attempted_completion_function = (rl_completion_func_t *) _completion; |
| |
| _read_history(cmd); |
| |
| _cmdline = cmdline; |
| |
| cmd->is_interactive = 1; |
| while (1) { |
| free(input); |
| input = readline("lvm> "); |
| |
| /* EOF */ |
| if (!input) { |
| /* readline sends prompt to stdout */ |
| printf("\n"); |
| break; |
| } |
| |
| /* empty line */ |
| if (!*input) |
| continue; |
| |
| add_history(input); |
| |
| argv = args; |
| |
| if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) { |
| log_error("Too many arguments, sorry."); |
| continue; |
| } |
| |
| if (!argc) |
| continue; |
| |
| if (!strcmp(argv[0], "lvm")) { |
| argv++; |
| argc--; |
| } |
| |
| if (!argc) |
| continue; |
| |
| if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) { |
| remove_history(history_length - 1); |
| log_error("Exiting."); |
| break; |
| } |
| |
| if (cmd->log_rh && strcmp(argv[0], "lastlog")) { |
| /* drop old log report */ |
| dm_report_free(cmd->log_rh); |
| cmd->log_rh = NULL; |
| } |
| |
| ret = lvm_run_command(cmd, argc, argv); |
| if (ret == ENO_SUCH_CMD) |
| log_error("No such command '%s'. Try 'help'.", |
| argv[0]); |
| |
| if ((ret != ECMD_PROCESSED) && !error_message_produced()) { |
| log_debug(INTERNAL_ERROR "Failed command did not use log_error"); |
| log_error("Command failed with status code %d.", ret); |
| } |
| _write_history(); |
| } |
| cmd->is_interactive = 0; |
| |
| free(input); |
| return 0; |
| } |
| |
| #endif /* READLINE_SUPPORT */ |