2 * core.c: Library for interactive commandline interfaces
4 * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <readline/readline.h>
24 #include <readline/history.h>
26 #include <libvscmisc/assert.h>
27 #include <libvscmisc/error.h>
28 #include <libvscmisc/memory.h>
30 #include "../include/libvsccli/command.h"
31 #include "../include/libvsccli/core.h"
32 #include "../include/libvsccli/help.h"
33 #include "../include/libvsccli/option.h"
37 static int _initialized = FALSE;
38 static int _verbose_errors = FALSE;
40 const char *_vsc_cli_name = NULL;
41 const char *_vsc_cli_help = NULL;
42 const struct VscCliOptionInfo *_vsc_cli_option_infos = NULL;
43 const struct VscCliCommandInfo **_vsc_cli_command_infos = NULL;
44 int _vsc_cli_output_coloring = TRUE;
45 int _vsc_cli_interactive = FALSE;
48 _quit (struct VscError *error, const struct VscCliCommandInfo *command_info,
49 const struct VscCliOption *option_list, void *user_data);
51 static const struct VscCliOptionInfo _quit_option_infos[] = {
52 { -1, NULL, NULL, 0, FALSE },
55 const struct VscCliCommandInfo vsc_cli_quit_command_info = {
57 "quit interactive mode",
64 _quit (struct VscError *error VSC__ATTR__UNUSED,
65 const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
66 const struct VscCliOption *option_list VSC__ATTR__UNUSED,
67 void *user_data VSC__ATTR__UNUSED)
69 _vsc_cli_interactive = FALSE;
73 _readline_command_generator (const char *text, int state)
75 static int last_index, text_length;
76 const struct VscCliCommandInfo *command_info = NULL;
80 text_length = strlen (text);
83 while (_vsc_cli_command_infos[last_index] != NULL) {
84 command_info = _vsc_cli_command_infos[last_index];
87 if (strncmp (text, command_info->name, text_length) == 0) {
88 return strdup (command_info->name);
96 _readline_options_generator(const char *text, int state)
98 static int last_index, text_length;
99 static const struct VscCliCommandInfo *command_info = NULL;
100 const struct VscCliCommandInfo *help_command_info = NULL;
102 char *command_name = NULL;
103 const struct VscCliOptionInfo *option_info;
107 ptr = strchr (rl_line_buffer, ' ');
113 command_name = calloc (1, (ptr - rl_line_buffer) + 1);
115 if (command_name == NULL) {
119 memcpy (command_name, rl_line_buffer, ptr - rl_line_buffer);
121 command_info = _vsc_cli_command_info_lookup (command_name);
123 text_length = strlen (text);
125 vsc_free (&command_name);
128 if (command_info == NULL) {
132 if (command_info->option_infos[0].long_name == NULL) {
136 if (strcmp (command_info->name, "help") == 0) {
137 while (_vsc_cli_command_infos[last_index] != NULL) {
138 help_command_info = _vsc_cli_command_infos[last_index];
141 if (strncmp (text, help_command_info->name, text_length) == 0) {
142 return strdup (help_command_info->name);
146 while (command_info->option_infos[last_index].long_name != NULL) {
147 option_info = &command_info->option_infos[last_index];
150 if (option_info->type == VSC_CLI__OPTION_TYPE__DATA) {
154 if (text_length > 2) {
155 if (strncmp (option_info->long_name, text + 2,
156 text_length - 2) != 0) {
161 result = calloc (strlen (option_info->long_name) + 3, 1);
163 if (result == NULL) {
167 snprintf (result, strlen (option_info->long_name) + 3, "--%s",
168 option_info->long_name);
178 _readline_completion (const char *text, int start, int end VSC__ATTR__UNUSED)
180 char **matches = (char **) NULL;
183 matches = rl_completion_matches(text, _readline_command_generator);
185 matches = rl_completion_matches(text, _readline_options_generator);
192 vsc_cli_init (struct VscError *error,
193 const char *name, const char *help,
194 const struct VscCliOptionInfo *option_infos,
195 const struct VscCliCommandInfo **command_infos,
198 VSC__ASSERT (error != NULL);
199 VSC__ASSERT (! error->occured);
200 VSC__ASSERT (name != NULL);
201 VSC__ASSERT (help != NULL);
202 VSC__ASSERT (option_infos != NULL);
203 VSC__ASSERT (command_infos != NULL);
206 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
207 "Already initialized");
211 if (_vsc_cli_option_infos_lookup_data (option_infos, 0) != 0) {
212 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
213 "Data options can not be used as direct options");
217 _vsc_cli_name = name;
218 _vsc_cli_help = help;
219 _vsc_cli_option_infos = option_infos;
220 _vsc_cli_command_infos = command_infos;
221 _vsc_cli_output_coloring = output_coloring;
223 rl_readline_name = name;
224 rl_attempted_completion_function = _readline_completion;
226 stifle_history (500);
232 vsc_cli_cleanup (void)
234 VSC__ASSERT (_initialized);
236 if (! _initialized) {
240 _vsc_cli_name = NULL;
241 _vsc_cli_help = NULL;
242 _vsc_cli_option_infos = NULL;
243 _vsc_cli_command_infos = NULL;
244 _vsc_cli_output_coloring = TRUE;
245 _vsc_cli_interactive = FALSE;
247 _initialized = FALSE;
251 vsc_cli_error_fprint (struct VscError *error, FILE *stream)
253 static const char *prefix_colored = "\033[01;31mError\033[0m";
254 static const char *prefix = "Error";
256 if (_verbose_errors) {
257 vsc_error_fprint (error, stream);
259 if (error->message != NULL) {
260 if (error->code == VSC__ERROR_CODE__REMOTE_ERROR) {
261 fprintf (stream, "%s: Remote: %s\n",
262 _vsc_cli_output_coloring ? prefix_colored : prefix,
265 fprintf (stream, "%s: %s\n",
266 _vsc_cli_output_coloring ? prefix_colored : prefix,
270 fprintf (stream, "%s: %s\n",
271 _vsc_cli_output_coloring ? prefix_colored : prefix,
272 vsc_error_string(error->code));
277 struct VscCliOption * /* option_list */
278 vsc_cli_parse_option_list (struct VscError *error, const char *input,
279 const char **input_tail)
282 struct VscCliOption option;
283 struct VscCliOption *option_list = NULL;
285 VSC__ASSERT (error != NULL);
286 VSC__ASSERT (! error->occured);
287 VSC__ASSERT (input != NULL);
288 VSC__ASSERT (input_tail != NULL);
292 while (*input_tail != NULL && **input_tail != '\0') {
294 _vsc_cli_option_parse (error, _vsc_cli_option_infos, -1, NULL,
295 *input_tail, input_tail, &option);
297 if (error->occured) {
298 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
303 if (strcmp (option.info->long_name,
304 VSC_CLI__VERBOSE_ERRORS__OPTION_INFO__LONG_NAME) == 0) {
305 _verbose_errors = TRUE;
306 } else if (strcmp (option.info->long_name,
307 VSC_CLI__NO_COLOR__OPTION_INFO__LONG_NAME) == 0) {
308 _vsc_cli_output_coloring = FALSE;
311 _vsc_cli_option_list_append (error, &option_list, &option);
312 vsc_free (&option.string);
314 if (error->occured) {
315 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
326 vsc_cli_option_list_free (&option_list);
331 struct VscCliCommand * /* command_list */
332 vsc_cli_parse_command_list (struct VscError *error, const char *input,
333 const char **input_tail)
336 struct VscCliCommand command;
337 struct VscCliCommand *command_list = NULL;
339 VSC__ASSERT (error != NULL);
340 VSC__ASSERT (! error->occured);
341 VSC__ASSERT (input != NULL);
342 VSC__ASSERT (input_tail != NULL);
346 while (*input_tail != NULL && **input_tail != '\0') {
348 vsc_cli_command_parse (error, *input_tail, input_tail, &command);
350 if (error->occured) {
351 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
355 if (command_parsed) {
356 _vsc_cli_command_list_append (error, &command_list, &command);
357 vsc_cli_option_list_free (&command.option_list);
359 if (error->occured) {
360 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
371 vsc_cli_command_list_free (&command_list);
377 vsc_cli_run_command_list (struct VscError *error,
378 const struct VscCliCommand *command_list,
381 struct VscCliCommand *command;
383 VSC__ASSERT (error != NULL);
384 VSC__ASSERT (! error->occured);
385 VSC__ASSERT (command_list != NULL);
387 for (command = (struct VscCliCommand *) command_list; command != NULL;
388 command = command->next) {
389 command->info->function (error, command->info, command->option_list,
392 if (error->occured) {
393 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
400 vsc_cli_enter_interactive (VscCliErrorFunction error_function,
401 VscCliPromptFunction prompt_function,
404 struct VscError error;
407 const char *input_tail = NULL;
408 struct VscCliCommand *command_list = NULL;
410 VSC__ASSERT (error_function != NULL);
411 VSC__ASSERT (prompt_function != NULL);
412 VSC__ASSERT (_vsc_cli_command_info_lookup ("quit") != NULL);
414 vsc_error_init (&error);
416 _vsc_cli_interactive = TRUE;
418 while (_vsc_cli_interactive) {
419 prompt_function (&error, prompt, 256, user_data);
422 error_function (&error, user_data);
426 input = readline (prompt);
432 if (*input == '\0') {
438 command_list = vsc_cli_parse_command_list
439 (&error, input, &input_tail);
442 error_function (&error, user_data);
446 if (command_list != NULL) {
447 vsc_cli_run_command_list (&error, command_list, user_data);
450 error_function (&error, user_data);
457 vsc_cli_command_list_free (&command_list);
458 vsc_error_cleanup (&error);
459 vsc_error_init (&error);
463 vsc_error_cleanup (&error);
465 _vsc_cli_interactive = FALSE;