2 * option.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 <libvscmisc/assert.h>
24 #include <libvscmisc/error.h>
25 #include <libvscmisc/list.h>
26 #include <libvscmisc/memory.h>
27 #include <libvscmisc/string.h>
29 #include "../include/libvsccli/option.h"
34 _vsc_cli_option_syntax (const struct VscCliOptionInfo *option_info,
37 static char buffer1[256];
38 static char buffer2[256];
39 static char buffer3[512];
41 VSC__ASSERT (option_info != NULL);
43 switch (option_info->type) {
44 case VSC_CLI__OPTION_TYPE__DATA:
45 snprintf (buffer3, 512, "<%s>", option_info->long_name);
48 case VSC_CLI__OPTION_TYPE__BOOL:
49 snprintf (buffer1, 256, "-%c", option_info->short_name);
50 snprintf (buffer2, 256, "--%s", option_info->long_name);
53 case VSC_CLI__OPTION_TYPE__NUMBER:
54 snprintf (buffer1, 256, "-%c <number>", option_info->short_name);
55 snprintf (buffer2, 256, "--%s <number>", option_info->long_name);
58 case VSC_CLI__OPTION_TYPE__STRING:
59 snprintf (buffer1, 256, "-%c <string>", option_info->short_name);
60 snprintf (buffer2, 256, "--%s <string>", option_info->long_name);
64 snprintf (buffer1, 256, "INVALID");
65 snprintf (buffer2, 256, "INVALID");
69 if (synopsis && option_info->short_name != -1) {
73 if (option_info->short_name == -1) {
77 snprintf (buffer3, 512, "%s, %s", buffer1, buffer2);
82 int /* option_parsed */
83 _vsc_cli_option_parse (struct VscError *error,
84 const struct VscCliOptionInfo *option_infos,
85 int data_option_index, const char *command_name,
86 const char *input, const char **input_tail,
87 struct VscCliOption *option)
89 int option_parsed = TRUE;
90 const struct VscCliOptionInfo *option_info;
91 const char *tail = NULL;
95 VSC__ASSERT (error != NULL);
96 VSC__ASSERT (! error->occured);
97 VSC__ASSERT (option_infos != NULL);
98 VSC__ASSERT (input != NULL);
99 VSC__ASSERT (input_tail != NULL);
100 VSC__ASSERT (option != NULL);
104 _vsc_cli_token_parse (error, input, &token, &tail);
106 if (error->occured) {
107 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
115 memset (option, 0, sizeof (struct VscCliOption));
120 if (*(token + 1) == '-') {
125 if (num_dashes > 0) {
126 if (num_dashes == 1 && *(token + 2) != '\0') {
127 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
128 "Invalid option syntax: %s", token);
132 option_info = _vsc_cli_option_infos_lookup
133 (option_infos, token + num_dashes, num_dashes > 1);
135 if (option_info == NULL) {
136 if (command_name != NULL) {
137 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
138 "Unknown option %s for command '%s'",
139 token, command_name);
141 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
142 "Unknown option %s", token);
148 option->info = option_info;
150 switch (option_info->type) {
151 case VSC_CLI__OPTION_TYPE__BOOL:
154 case VSC_CLI__OPTION_TYPE__NUMBER:
156 _vsc_cli_token_parse (error, tail, &token, &tail);
158 if (error->occured) {
159 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
164 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
165 "Expected option syntax: %s",
166 _vsc_cli_option_syntax (option_info, FALSE));
170 option->number = vsc_strtoi (error, token, NULL, 10);
172 if (error->occured) {
173 vsc_error_cleanup (error);
174 vsc_error_init (error);
176 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
177 "Expected option syntax: %s",
178 _vsc_cli_option_syntax (option_info, FALSE));
184 case VSC_CLI__OPTION_TYPE__STRING:
186 _vsc_cli_token_parse (error, tail, &token, &tail);
188 if (error->occured) {
189 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
194 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
195 "Expected option syntax: %s",
196 _vsc_cli_option_syntax (option_info, FALSE));
200 option->string = token;
205 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
210 if (error->occured) {
211 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
214 } else if (data_option_index >= 0) {
215 option_info = _vsc_cli_option_infos_lookup_data
216 (option_infos, data_option_index);
218 if (option_info == NULL) {
219 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
220 "Too many options for command '%s'",
225 option->info = option_info;
226 option->string = token;
229 if (error->occured) {
230 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
234 option_parsed = FALSE;
244 return option_parsed;
247 vsc_free (&option->string);
249 option_parsed = FALSE;
254 static const struct VscCliOption *
255 _option_get (const struct VscCliOption *option_list,
256 const char *option_long_name)
258 const struct VscCliOption *option;
260 VSC__ASSERT (option_long_name != NULL);
262 for (option = option_list; option != NULL; option = option->next) {
263 if (strcmp (option->info->long_name, option_long_name) == 0) {
272 vsc_cli_option_get_bool (const struct VscCliOption *option_list,
273 const char *option_long_name)
275 return _option_get (option_list, option_long_name) != NULL;
279 vsc_cli_option_get_number (const struct VscCliOption *option_list,
280 const char *option_long_name, int *found)
282 const struct VscCliOption *option;
284 VSC__ASSERT (option_long_name != NULL);
290 option = _option_get (option_list, option_long_name);
292 if (option == NULL) {
300 return option->number;
304 vsc_cli_option_get_string (const struct VscCliOption *option_list,
305 const char *option_long_name)
307 const struct VscCliOption *option;
309 VSC__ASSERT (option_long_name != NULL);
311 option = _option_get (option_list, option_long_name);
313 if (option == NULL) {
317 return option->string;
320 const struct VscCliOptionInfo *
321 _vsc_cli_option_infos_lookup (const struct VscCliOptionInfo *option_infos,
322 const char *option_name, int is_long)
326 VSC__ASSERT (option_infos != NULL);
327 VSC__ASSERT (option_name != NULL);
329 for (i = 0; option_infos[i].long_name != NULL; i++) {
330 if (option_infos[i].type == VSC_CLI__OPTION_TYPE__DATA) {
335 if (strcmp (option_name, option_infos[i].long_name) == 0) {
336 return &option_infos[i];
339 if (*option_name == option_infos[i].short_name) {
340 return &option_infos[i];
348 const struct VscCliOptionInfo *
349 _vsc_cli_option_infos_lookup_data (const struct VscCliOptionInfo *option_infos,
354 VSC__ASSERT (option_infos != NULL);
355 VSC__ASSERT (position >= 0);
357 for (i = 0; option_infos[i].long_name != NULL; i++) {
358 if (option_infos[i].type == VSC_CLI__OPTION_TYPE__DATA) {
362 return &option_infos[i];
371 _option_free (struct VscCliOption **option)
373 VSC__ASSERT (option != NULL);
375 if (*option != NULL) {
376 vsc_free (&(*option)->string);
382 static struct VscCliOption * /* option_duplicate */
383 _option_duplicate (struct VscError *error, const struct VscCliOption *option)
385 struct VscCliOption *option_duplicate = NULL;
387 VSC__ASSERT (error != NULL);
388 VSC__ASSERT (! error->occured);
389 VSC__ASSERT (option != NULL);
391 option_duplicate = vsc_memdup (error, option, sizeof (struct VscCliOption));
393 if (error->occured) {
394 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
398 if (option->string != NULL) {
399 option_duplicate->string = vsc_strdup (error, option->string);
402 if (error->occured) {
403 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
407 option_duplicate->next = NULL;
409 return option_duplicate;
412 _option_free (&option_duplicate);
418 _option_match (struct VscList *item, void *match_data)
420 struct VscCliOption *option = (struct VscCliOption *) item;
421 const char *option_long_name = (const char *) match_data;
423 VSC__ASSERT (item != NULL);
425 return strcmp (option->info->long_name, option_long_name) == 0;
429 _vsc_cli_option_list_append (struct VscError *error,
430 struct VscCliOption **option_list,
431 const struct VscCliOption *option)
433 VSC__ASSERT (error != NULL);
434 VSC__ASSERT (! error->occured);
435 VSC__ASSERT (option_list != NULL);
436 VSC__ASSERT (option != NULL);
439 (error, (struct VscList **) option_list,
440 (struct VscList *) option,
441 (VscList_DuplicateFunction) _option_duplicate);
443 if (error->occured) {
444 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
450 vsc_cli_option_list_free (struct VscCliOption **option_list)
452 VSC__ASSERT (option_list != NULL);
454 vsc_list_free ((struct VscList **) option_list,
455 (VscList_FreeFunction) _option_free);
458 struct VscCliOption * /* option_list_duplicate */
459 _vsc_cli_option_list_duplicate (struct VscError *error,
460 const struct VscCliOption *option_list)
462 struct VscCliOption *option_list_duplicate = NULL;
464 VSC__ASSERT (error != NULL);
465 VSC__ASSERT (! error->occured);
467 option_list_duplicate = (struct VscCliOption *)
469 (error, (struct VscList *) option_list,
470 (VscList_DuplicateFunction) _option_duplicate,
471 (VscList_FreeFunction) _option_free);
473 if (error->occured) {
474 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
478 return option_list_duplicate;
481 struct VscCliOption *
482 _vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
483 const char *option_long_name)
485 VSC__ASSERT (option_long_name != NULL);
487 return (struct VscCliOption *)
489 ((struct VscList *) option_list,
490 (VscList_MatchFunction) _option_match,
491 (void *) option_long_name);