1 /* $NetBSD: lvm.c,v 1.1.1.2 2009/02/18 11:17:44 haad Exp $ */ 2 3 /* 4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. 5 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. 6 * 7 * This file is part of LVM2. 8 * 9 * This copyrighted material is made available to anyone wishing to use, 10 * modify, copy, or redistribute it subject to the terms and conditions 11 * of the GNU General Public License v.2. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program; if not, write to the Free Software Foundation, 15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 */ 17 18 #include "tools.h" 19 #include "lvm2cmdline.h" 20 21 int main(int argc, char **argv) 22 { 23 return lvm2_main(argc, argv); 24 } 25 26 #ifdef READLINE_SUPPORT 27 28 # include <readline/readline.h> 29 # include <readline/history.h> 30 # ifndef HAVE_RL_COMPLETION_MATCHES 31 # define rl_completion_matches(a, b) completion_matches((char *)a, b) 32 # endif 33 34 static struct cmdline_context *_cmdline; 35 36 /* List matching commands */ 37 static char *_list_cmds(const char *text, int state) 38 { 39 static int i = 0; 40 static size_t len = 0; 41 42 /* Initialise if this is a new completion attempt */ 43 if (!state) { 44 i = 0; 45 len = strlen(text); 46 } 47 48 while (i < _cmdline->num_commands) 49 if (!strncmp(text, _cmdline->commands[i++].name, len)) 50 return strdup(_cmdline->commands[i - 1].name); 51 52 return NULL; 53 } 54 55 /* List matching arguments */ 56 static char *_list_args(const char *text, int state) 57 { 58 static int match_no = 0; 59 static size_t len = 0; 60 static struct command *com; 61 62 /* Initialise if this is a new completion attempt */ 63 if (!state) { 64 char *s = rl_line_buffer; 65 int j = 0; 66 67 match_no = 0; 68 com = NULL; 69 len = strlen(text); 70 71 /* Find start of first word in line buffer */ 72 while (isspace(*s)) 73 s++; 74 75 /* Look for word in list of commands */ 76 for (j = 0; j < _cmdline->num_commands; j++) { 77 const char *p; 78 char *q = s; 79 80 p = _cmdline->commands[j].name; 81 while (*p == *q) { 82 p++; 83 q++; 84 } 85 if ((!*p) && *q == ' ') { 86 com = _cmdline->commands + j; 87 break; 88 } 89 } 90 91 if (!com) 92 return NULL; 93 } 94 95 /* Short form arguments */ 96 if (len < 3) { 97 while (match_no < com->num_args) { 98 char s[3]; 99 char c; 100 if (!(c = (_cmdline->the_args + 101 com->valid_args[match_no++])->short_arg)) 102 continue; 103 104 sprintf(s, "-%c", c); 105 if (!strncmp(text, s, len)) 106 return strdup(s); 107 } 108 } 109 110 /* Long form arguments */ 111 if (match_no < com->num_args) 112 match_no = com->num_args; 113 114 while (match_no - com->num_args < com->num_args) { 115 const char *l; 116 l = (_cmdline->the_args + 117 com->valid_args[match_no++ - com->num_args])->long_arg; 118 if (*(l + 2) && !strncmp(text, l, len)) 119 return strdup(l); 120 } 121 122 return NULL; 123 } 124 125 /* Custom completion function */ 126 static char **_completion(const char *text, int start_pos, 127 int end_pos __attribute((unused))) 128 { 129 char **match_list = NULL; 130 int p = 0; 131 132 while (isspace((int) *(rl_line_buffer + p))) 133 p++; 134 135 /* First word should be one of our commands */ 136 if (start_pos == p) 137 match_list = rl_completion_matches(text, _list_cmds); 138 139 else if (*text == '-') 140 match_list = rl_completion_matches(text, _list_args); 141 /* else other args */ 142 143 /* No further completion */ 144 rl_attempted_completion_over = 1; 145 return match_list; 146 } 147 148 static int _hist_file(char *buffer, size_t size) 149 { 150 char *e = getenv("HOME"); 151 152 if (dm_snprintf(buffer, size, "%s/.lvm_history", e) < 0) { 153 log_error("$HOME/.lvm_history: path too long"); 154 return 0; 155 } 156 157 return 1; 158 } 159 160 static void _read_history(struct cmd_context *cmd) 161 { 162 char hist_file[PATH_MAX]; 163 164 if (!_hist_file(hist_file, sizeof(hist_file))) 165 return; 166 167 if (read_history(hist_file)) 168 log_very_verbose("Couldn't read history from %s.", hist_file); 169 170 stifle_history(find_config_tree_int(cmd, "shell/history_size", 171 DEFAULT_MAX_HISTORY)); 172 173 } 174 175 static void _write_history(void) 176 { 177 char hist_file[PATH_MAX]; 178 179 if (!_hist_file(hist_file, sizeof(hist_file))) 180 return; 181 182 if (write_history(hist_file)) 183 log_very_verbose("Couldn't write history to %s.", hist_file); 184 } 185 186 int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline) 187 { 188 int argc, ret; 189 char *input = NULL, *args[MAX_ARGS], **argv; 190 191 rl_readline_name = "lvm"; 192 rl_attempted_completion_function = (CPPFunction *) _completion; 193 194 _read_history(cmd); 195 196 _cmdline = cmdline; 197 198 _cmdline->interactive = 1; 199 while (1) { 200 free(input); 201 input = readline("lvm> "); 202 203 /* EOF */ 204 if (!input) { 205 printf("\n"); 206 break; 207 } 208 209 /* empty line */ 210 if (!*input) 211 continue; 212 213 add_history(input); 214 215 argv = args; 216 217 if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) { 218 log_error("Too many arguments, sorry."); 219 continue; 220 } 221 222 if (!strcmp(argv[0], "lvm")) { 223 argv++; 224 argc--; 225 } 226 227 if (!argc) 228 continue; 229 230 if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) { 231 remove_history(history_length - 1); 232 log_error("Exiting."); 233 break; 234 } 235 236 ret = lvm_run_command(cmd, argc, argv); 237 if (ret == ENO_SUCH_CMD) 238 log_error("No such command '%s'. Try 'help'.", 239 argv[0]); 240 241 if ((ret != ECMD_PROCESSED) && !error_message_produced()) { 242 log_debug("Internal error: Failed command did not use log_error"); 243 log_error("Command failed with status code %d.", ret); 244 } 245 _write_history(); 246 } 247 248 free(input); 249 return 0; 250 } 251 252 #endif /* READLINE_SUPPORT */ 253