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
27 #include <libvscmisc/assert.h>
28 #include <libvscmisc/error.h>
29 #include <libvscmisc/list.h>
30 #include <libvscmisc/memory.h>
31 #include <libvscmisc/string.h>
33 #include "../include/libvsccli/option.h"
38 _vsc_cli_option_syntax (const struct VscCliOptionInfo *option_info,
41 const char *type = NULL;
42 static char syntax[512];
44 VSC__ASSERT (option_info != NULL);
46 if (option_info->flags & VSC_CLI__OPTION_FLAG__TAGGED) {
47 if (option_info->type == VSC_CLI__OPTION_TYPE__BOOLEAN) {
49 snprintf (syntax, 512, "--%s", option_info->long_name);
51 if (option_info->short_name > 0) {
52 snprintf (syntax, 512, "--%s, -%c", option_info->long_name,
53 option_info->short_name);
55 snprintf (syntax, 512, "--%s", option_info->long_name);
59 switch (option_info->type) {
60 case VSC_CLI__OPTION_TYPE__BOOLEAN:
64 case VSC_CLI__OPTION_TYPE__NUMBER:
68 case VSC_CLI__OPTION_TYPE__STRING:
78 snprintf (syntax, 512, "--%s %s", option_info->long_name,
81 if (option_info->short_name > 0) {
82 snprintf (syntax, 512, "--%s %s, -%c %s",
83 option_info->long_name, type,
84 option_info->short_name, type);
86 snprintf (syntax, 512, "--%s %s", option_info->long_name,
92 snprintf (syntax, 512, "<%s>", option_info->long_name);
98 int /* option_parsed */
99 _vsc_cli_option_parse (struct VscError *error,
100 const struct VscCliOptionInfo *option_infos,
101 int untagged_option_index, const char *command_name,
102 const char *input, const char **input_tail,
103 struct VscCliOption *option)
105 int option_parsed = TRUE;
106 const char *tail = NULL;
110 VSC__ASSERT (error != NULL);
111 VSC__ASSERT (! error->occured);
112 VSC__ASSERT (option_infos != NULL);
113 VSC__ASSERT (input != NULL);
114 VSC__ASSERT (input_tail != NULL);
115 VSC__ASSERT (option != NULL);
119 _vsc_cli_token_parse (error, input, &token, &tail);
121 if (error->occured) {
122 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
130 memset (option, 0, sizeof (struct VscCliOption));
135 if (*(token + 1) == '-') {
140 if (num_dashes > 0) {
141 if (num_dashes == 1 && *(token + 2) != '\0') {
142 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
143 "Invalid option syntax: %s", token);
147 option->info = _vsc_cli_option_infos_lookup_tagged
148 (option_infos, token + num_dashes, num_dashes > 1);
150 if (option->info == NULL) {
151 if (command_name != NULL) {
152 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
153 "Unknown option %s for command '%s'",
154 token, command_name);
156 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
157 "Unknown option %s", token);
163 switch (option->info->type) {
164 case VSC_CLI__OPTION_TYPE__BOOLEAN:
167 case VSC_CLI__OPTION_TYPE__NUMBER:
169 _vsc_cli_token_parse (error, tail, &token, &tail);
171 if (error->occured) {
172 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
177 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
178 "Expected option syntax: %s",
179 _vsc_cli_option_syntax (option->info, FALSE));
183 option->number = vsc_strtoi (error, token, NULL, 10);
185 if (error->occured) {
186 VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
187 "Expected option syntax: %s",
188 _vsc_cli_option_syntax (option->info, FALSE));
194 case VSC_CLI__OPTION_TYPE__STRING:
196 _vsc_cli_token_parse (error, tail, &token, &tail);
198 if (error->occured) {
199 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
204 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
205 "Expected option syntax: %s",
206 _vsc_cli_option_syntax (option->info, FALSE));
210 option->string = token;
215 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
220 if (error->occured) {
221 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
224 } else if (untagged_option_index >= 0) {
225 option->info = _vsc_cli_option_infos_lookup_untagged
226 (option_infos, untagged_option_index);
228 if (option->info == NULL) {
229 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
230 "Too many untagged options for command '%s'",
235 switch (option->info->type) {
236 case VSC_CLI__OPTION_TYPE__BOOLEAN:
237 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
238 "Boolean options can not untagged");
242 case VSC_CLI__OPTION_TYPE__NUMBER:
243 option->number = vsc_strtoi (error, token, NULL, 10);
245 if (error->occured) {
246 VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
247 "Expected option syntax: %s",
248 _vsc_cli_option_syntax (option->info, FALSE));
254 case VSC_CLI__OPTION_TYPE__STRING:
255 option->string = token;
260 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
265 if (error->occured) {
266 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
270 option_parsed = FALSE;
280 return option_parsed;
283 vsc_free (&option->string);
284 memset (option, 0, sizeof (struct VscCliOption));
286 option_parsed = FALSE;
291 const struct VscCliOption *
292 vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
293 const char *option_long_name)
295 const struct VscCliOption *option;
297 VSC__ASSERT (option_long_name != NULL);
299 for (option = option_list; option != NULL; option = option->next) {
300 if (strcmp (option->info->long_name, option_long_name) == 0) {
309 vsc_cli_option_list_lookup_boolean (const struct VscCliOption *option_list,
310 const char *option_long_name)
312 const struct VscCliOption *option;
314 option = vsc_cli_option_list_lookup (option_list, option_long_name);
316 if (option != NULL) {
317 VSC__ASSERT (option->info->type == VSC_CLI__OPTION_TYPE__BOOLEAN);
326 vsc_cli_option_list_lookup_number (const struct VscCliOption *option_list,
327 const char *option_long_name, int *found)
329 const struct VscCliOption *option;
331 VSC__ASSERT (option_long_name != NULL);
337 option = vsc_cli_option_list_lookup (option_list, option_long_name);
339 if (option == NULL) {
343 VSC__ASSERT (option->info->type == VSC_CLI__OPTION_TYPE__NUMBER);
349 return option->number;
353 vsc_cli_option_list_lookup_string (const struct VscCliOption *option_list,
354 const char *option_long_name)
356 const struct VscCliOption *option;
358 VSC__ASSERT (option_long_name != NULL);
360 option = vsc_cli_option_list_lookup (option_list, option_long_name);
362 if (option == NULL) {
366 VSC__ASSERT (option->info->type == VSC_CLI__OPTION_TYPE__STRING);
368 return option->string;
371 const struct VscCliOptionInfo *
372 _vsc_cli_option_infos_lookup_tagged
373 (const struct VscCliOptionInfo *option_infos, const char *option_name,
378 VSC__ASSERT (option_infos != NULL);
379 VSC__ASSERT (option_name != NULL);
381 for (i = 0; option_infos[i].long_name != NULL; i++) {
382 if (! (option_infos[i].flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
387 if (strcmp (option_name, option_infos[i].long_name) == 0) {
388 return &option_infos[i];
391 if (*option_name == option_infos[i].short_name) {
392 return &option_infos[i];
400 const struct VscCliOptionInfo *
401 _vsc_cli_option_infos_lookup_untagged
402 (const struct VscCliOptionInfo *option_infos, int position)
406 VSC__ASSERT (option_infos != NULL);
407 VSC__ASSERT (position >= 0);
409 for (i = 0; option_infos[i].long_name != NULL; i++) {
410 if (! (option_infos[i].flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
414 return &option_infos[i];
423 _option_free (struct VscCliOption **option)
425 VSC__ASSERT (option != NULL);
427 if (*option != NULL) {
428 vsc_free (&(*option)->string);
434 static struct VscCliOption * /* option_clone */
435 _option_clone (struct VscError *error, const struct VscCliOption *option)
437 struct VscCliOption *option_clone = NULL;
439 VSC__ASSERT (error != NULL);
440 VSC__ASSERT (! error->occured);
441 VSC__ASSERT (option != NULL);
443 option_clone = vsc_memdup (error, option, sizeof (struct VscCliOption));
445 if (error->occured) {
446 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
450 option_clone->next = NULL;
452 if (option->string != NULL) {
453 option_clone->string = vsc_strdup (error, option->string);
456 if (error->occured) {
457 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
464 _option_free (&option_clone);
470 _option_match (struct VscList *item, void *match_data)
472 struct VscCliOption *option = (struct VscCliOption *) item;
473 const char *option_long_name = (const char *) match_data;
475 VSC__ASSERT (item != NULL);
477 return strcmp (option->info->long_name, option_long_name) == 0;
481 _vsc_cli_option_list_append_clone (struct VscError *error,
482 struct VscCliOption **option_list,
483 const struct VscCliOption *option)
485 VSC__ASSERT (error != NULL);
486 VSC__ASSERT (! error->occured);
487 VSC__ASSERT (option_list != NULL);
488 VSC__ASSERT (option != NULL);
490 vsc_list_append_clone
491 (error, (struct VscList **) option_list,
492 (struct VscList *) option, (VscList_CloneFunction) _option_clone);
494 if (error->occured) {
495 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
501 vsc_cli_option_list_free (struct VscCliOption **option_list)
503 VSC__ASSERT (option_list != NULL);
505 vsc_list_free ((struct VscList **) option_list,
506 (VscList_FreeFunction) _option_free);
509 struct VscCliOption * /* option_list_clone */
510 _vsc_cli_option_list_clone (struct VscError *error,
511 const struct VscCliOption *option_list)
513 struct VscCliOption *option_list_clone = NULL;
515 VSC__ASSERT (error != NULL);
516 VSC__ASSERT (! error->occured);
518 option_list_clone = (struct VscCliOption *)
520 (error, (struct VscList *) option_list,
521 (VscList_CloneFunction) _option_clone,
522 (VscList_FreeFunction) _option_free);
524 if (error->occured) {
525 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
529 return option_list_clone;
532 struct VscCliOption *
533 _vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
534 const char *option_long_name)
536 VSC__ASSERT (option_long_name != NULL);
538 return (struct VscCliOption *)
540 ((struct VscList *) option_list,
541 (VscList_MatchFunction) _option_match,
542 (void *) option_long_name);