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