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 VSC_CLI__NULL__OPTION_INFO,
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->tagged) {
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,
199 const struct VscCliCommandInfo *command_info;
200 const struct VscCliOptionInfo *option_info;
202 VSC__ASSERT (error != NULL);
203 VSC__ASSERT (! error->occured);
204 VSC__ASSERT (name != NULL);
205 VSC__ASSERT (help != NULL);
206 VSC__ASSERT (option_infos != NULL);
207 VSC__ASSERT (command_infos != NULL);
210 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
211 "Already initialized");
215 for (i = 0; command_infos[i] != NULL; i++) {
216 command_info = command_infos[i];
218 for (k = 0; command_info->option_infos[k].long_name != NULL; k++) {
219 option_info = &command_info->option_infos[k];
221 if (option_info->type == VSC_CLI__OPTION_TYPE__BOOLEAN &&
222 ! option_info->tagged) {
223 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
224 "Boolean options can not untagged");
230 if (_vsc_cli_option_infos_lookup_untagged (option_infos, 0) != NULL) {
231 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
232 "Untagged options can not be used as direct options");
236 _vsc_cli_name = name;
237 _vsc_cli_help = help;
238 _vsc_cli_option_infos = option_infos;
239 _vsc_cli_command_infos = command_infos;
240 _vsc_cli_output_coloring = output_coloring;
242 rl_readline_name = name;
243 rl_attempted_completion_function = _readline_completion;
245 stifle_history (500);
251 vsc_cli_cleanup (void)
253 VSC__ASSERT (_initialized);
255 if (! _initialized) {
259 _vsc_cli_name = NULL;
260 _vsc_cli_help = NULL;
261 _vsc_cli_option_infos = NULL;
262 _vsc_cli_command_infos = NULL;
263 _vsc_cli_output_coloring = TRUE;
264 _vsc_cli_interactive = FALSE;
266 _initialized = FALSE;
270 vsc_cli_error_fprint (struct VscError *error, FILE *stream)
272 static const char *prefix_colored = "\033[01;31mError\033[0m";
273 static const char *prefix = "Error";
275 if (_verbose_errors) {
276 vsc_error_fprint (error, stream);
278 if (error->message != NULL) {
279 if (error->code == VSC__ERROR_CODE__REMOTE_ERROR) {
280 fprintf (stream, "%s: Remote: %s\n",
281 _vsc_cli_output_coloring ? prefix_colored : prefix,
284 fprintf (stream, "%s: %s\n",
285 _vsc_cli_output_coloring ? prefix_colored : prefix,
289 fprintf (stream, "%s: %s\n",
290 _vsc_cli_output_coloring ? prefix_colored : prefix,
291 vsc_error_string(error->code));
296 struct VscCliOption * /* option_list */
297 vsc_cli_parse_option_list (struct VscError *error, const char *input,
298 const char **input_tail)
301 struct VscCliOption option;
302 struct VscCliOption *option_list = NULL;
304 VSC__ASSERT (error != NULL);
305 VSC__ASSERT (! error->occured);
306 VSC__ASSERT (input != NULL);
307 VSC__ASSERT (input_tail != NULL);
311 while (*input_tail != NULL && **input_tail != '\0') {
313 _vsc_cli_option_parse (error, _vsc_cli_option_infos, -1, NULL,
314 *input_tail, input_tail, &option);
316 if (error->occured) {
317 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
322 if (strcmp (option.info->long_name,
323 VSC_CLI__VERBOSE_ERRORS__OPTION_INFO__LONG_NAME) == 0) {
324 _verbose_errors = TRUE;
325 } else if (strcmp (option.info->long_name,
326 VSC_CLI__NO_COLOR__OPTION_INFO__LONG_NAME) == 0) {
327 _vsc_cli_output_coloring = FALSE;
330 _vsc_cli_option_list_append (error, &option_list, &option);
331 vsc_free (&option.string);
333 if (error->occured) {
334 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
345 vsc_cli_option_list_free (&option_list);
350 struct VscCliCommand * /* command_list */
351 vsc_cli_parse_command_list (struct VscError *error, const char *input,
352 const char **input_tail)
355 struct VscCliCommand command;
356 struct VscCliCommand *command_list = NULL;
358 VSC__ASSERT (error != NULL);
359 VSC__ASSERT (! error->occured);
360 VSC__ASSERT (input != NULL);
361 VSC__ASSERT (input_tail != NULL);
365 while (*input_tail != NULL && **input_tail != '\0') {
367 vsc_cli_command_parse (error, *input_tail, input_tail, &command);
369 if (error->occured) {
370 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
374 if (command_parsed) {
375 _vsc_cli_command_list_append (error, &command_list, &command);
376 vsc_cli_option_list_free (&command.option_list);
378 if (error->occured) {
379 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
390 vsc_cli_command_list_free (&command_list);
396 vsc_cli_run_command_list (struct VscError *error,
397 const struct VscCliCommand *command_list,
400 struct VscCliCommand *command;
402 VSC__ASSERT (error != NULL);
403 VSC__ASSERT (! error->occured);
404 VSC__ASSERT (command_list != NULL);
406 for (command = (struct VscCliCommand *) command_list; command != NULL;
407 command = command->next) {
408 command->info->function (error, command->info, command->option_list,
411 if (error->occured) {
412 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
419 vsc_cli_enter_interactive (VscCliErrorFunction error_function,
420 VscCliPromptFunction prompt_function,
423 struct VscError error;
426 const char *input_tail = NULL;
427 struct VscCliCommand *command_list = NULL;
429 VSC__ASSERT (error_function != NULL);
430 VSC__ASSERT (prompt_function != NULL);
431 VSC__ASSERT (_vsc_cli_command_info_lookup ("quit") != NULL);
433 vsc_error_init (&error);
435 _vsc_cli_interactive = TRUE;
437 while (_vsc_cli_interactive) {
438 prompt_function (&error, prompt, 256, user_data);
441 error_function (&error, user_data);
445 input = readline (prompt);
451 if (*input == '\0') {
457 command_list = vsc_cli_parse_command_list
458 (&error, input, &input_tail);
461 error_function (&error, user_data);
465 if (command_list != NULL) {
466 vsc_cli_run_command_list (&error, command_list, user_data);
469 error_function (&error, user_data);
476 vsc_cli_command_list_free (&command_list);
477 vsc_error_cleanup (&error);
478 vsc_error_init (&error);
482 vsc_error_cleanup (&error);
484 _vsc_cli_interactive = FALSE;