xref: /dragonfly/contrib/lvm2/dist/tools/lvm.c (revision ef2b2b9d)
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