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 const char *type = NULL;
38 static char syntax[512];
40 VSC__ASSERT (option_info != NULL);
42 if (option_info->flags & VSC_CLI__OPTION_FLAG__TAGGED) {
43 if (option_info->type == VSC_CLI__OPTION_TYPE__BOOLEAN) {
45 snprintf (syntax, 512, "--%s", option_info->long_name);
47 if (option_info->short_name > 0) {
48 snprintf (syntax, 512, "--%s, -%c", option_info->long_name,
49 option_info->short_name);
51 snprintf (syntax, 512, "--%s", option_info->long_name);
55 switch (option_info->type) {
56 case VSC_CLI__OPTION_TYPE__BOOLEAN:
60 case VSC_CLI__OPTION_TYPE__NUMBER:
64 case VSC_CLI__OPTION_TYPE__STRING:
74 snprintf (syntax, 512, "--%s %s", option_info->long_name,
77 if (option_info->short_name > 0) {
78 snprintf (syntax, 512, "--%s %s, -%c %s",
79 option_info->long_name, type,
80 option_info->short_name, type);
82 snprintf (syntax, 512, "--%s %s", option_info->long_name,
88 snprintf (syntax, 512, "<%s>", option_info->long_name);
94 int /* option_parsed */
95 _vsc_cli_option_parse (struct VscError *error,
96 const struct VscCliOptionInfo *option_infos,
97 int untagged_option_index, const char *command_name,
98 const char *input, const char **input_tail,
99 struct VscCliOption *option)
101 int option_parsed = TRUE;
102 const struct VscCliOptionInfo *option_info;
103 const char *tail = NULL;
107 VSC__ASSERT (error != NULL);
108 VSC__ASSERT (! error->occured);
109 VSC__ASSERT (option_infos != NULL);
110 VSC__ASSERT (input != NULL);
111 VSC__ASSERT (input_tail != NULL);
112 VSC__ASSERT (option != NULL);
116 _vsc_cli_token_parse (error, input, &token, &tail);
118 if (error->occured) {
119 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
127 memset (option, 0, sizeof (struct VscCliOption));
132 if (*(token + 1) == '-') {
137 if (num_dashes > 0) {
138 if (num_dashes == 1 && *(token + 2) != '\0') {
139 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
140 "Invalid option syntax: %s", token);
144 option_info = _vsc_cli_option_infos_lookup_tagged
145 (option_infos, token + num_dashes, num_dashes > 1);
147 if (option_info == NULL) {
148 if (command_name != NULL) {
149 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
150 "Unknown option %s for command '%s'",
151 token, command_name);
153 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
154 "Unknown option %s", token);
160 option->info = option_info;
162 switch (option_info->type) {
163 case VSC_CLI__OPTION_TYPE__BOOLEAN:
166 case VSC_CLI__OPTION_TYPE__NUMBER:
168 _vsc_cli_token_parse (error, tail, &token, &tail);
170 if (error->occured) {
171 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
176 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
177 "Expected option syntax: %s",
178 _vsc_cli_option_syntax (option_info, FALSE));
182 option->number = vsc_strtoi (error, token, NULL, 10);
184 if (error->occured) {
185 vsc_error_cleanup (error);
186 vsc_error_init (error);
188 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
189 "Expected option syntax: %s",
190 _vsc_cli_option_syntax (option_info, FALSE));
196 case VSC_CLI__OPTION_TYPE__STRING:
198 _vsc_cli_token_parse (error, tail, &token, &tail);
200 if (error->occured) {
201 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
206 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
207 "Expected option syntax: %s",
208 _vsc_cli_option_syntax (option_info, FALSE));
212 option->string = token;
217 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
222 if (error->occured) {
223 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
226 } else if (untagged_option_index >= 0) {
227 option_info = _vsc_cli_option_infos_lookup_untagged
228 (option_infos, untagged_option_index);
230 if (option_info == NULL) {
231 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
232 "Too many untagged options for command '%s'",
237 option->info = option_info;
238 option->string = token;
241 if (error->occured) {
242 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
246 option_parsed = FALSE;
256 return option_parsed;
259 vsc_free (&option->string);
261 option_parsed = FALSE;
266 const struct VscCliOption *
267 vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
268 const char *option_long_name)
270 const struct VscCliOption *option;
272 VSC__ASSERT (option_long_name != NULL);
274 for (option = option_list; option != NULL; option = option->next) {
275 if (strcmp (option->info->long_name, option_long_name) == 0) {
284 vsc_cli_option_list_lookup_boolean (const struct VscCliOption *option_list,
285 const char *option_long_name)
287 return vsc_cli_option_list_lookup (option_list, option_long_name) != NULL;
291 vsc_cli_option_list_lookup_number (const struct VscCliOption *option_list,
292 const char *option_long_name, int *found)
294 const struct VscCliOption *option;
296 VSC__ASSERT (option_long_name != NULL);
302 option = vsc_cli_option_list_lookup (option_list, option_long_name);
304 if (option == NULL) {
312 return option->number;
316 vsc_cli_option_list_lookup_string (const struct VscCliOption *option_list,
317 const char *option_long_name)
319 const struct VscCliOption *option;
321 VSC__ASSERT (option_long_name != NULL);
323 option = vsc_cli_option_list_lookup (option_list, option_long_name);
325 if (option == NULL) {
329 return option->string;
332 const struct VscCliOptionInfo *
333 _vsc_cli_option_infos_lookup_tagged
334 (const struct VscCliOptionInfo *option_infos, const char *option_name,
339 VSC__ASSERT (option_infos != NULL);
340 VSC__ASSERT (option_name != NULL);
342 for (i = 0; option_infos[i].long_name != NULL; i++) {
343 if (! (option_infos[i].flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
348 if (strcmp (option_name, option_infos[i].long_name) == 0) {
349 return &option_infos[i];
352 if (*option_name == option_infos[i].short_name) {
353 return &option_infos[i];
361 const struct VscCliOptionInfo *
362 _vsc_cli_option_infos_lookup_untagged
363 (const struct VscCliOptionInfo *option_infos, int position)
367 VSC__ASSERT (option_infos != NULL);
368 VSC__ASSERT (position >= 0);
370 for (i = 0; option_infos[i].long_name != NULL; i++) {
371 if (! (option_infos[i].flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
375 return &option_infos[i];
384 _option_free (struct VscCliOption **option)
386 VSC__ASSERT (option != NULL);
388 if (*option != NULL) {
389 vsc_free (&(*option)->string);
395 static struct VscCliOption * /* option_duplicate */
396 _option_duplicate (struct VscError *error, const struct VscCliOption *option)
398 struct VscCliOption *option_duplicate = NULL;
400 VSC__ASSERT (error != NULL);
401 VSC__ASSERT (! error->occured);
402 VSC__ASSERT (option != NULL);
404 option_duplicate = vsc_memdup (error, option, sizeof (struct VscCliOption));
406 if (error->occured) {
407 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
411 if (option->string != NULL) {
412 option_duplicate->string = vsc_strdup (error, option->string);
415 if (error->occured) {
416 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
420 option_duplicate->next = NULL;
422 return option_duplicate;
425 _option_free (&option_duplicate);
431 _option_match (struct VscList *item, void *match_data)
433 struct VscCliOption *option = (struct VscCliOption *) item;
434 const char *option_long_name = (const char *) match_data;
436 VSC__ASSERT (item != NULL);
438 return strcmp (option->info->long_name, option_long_name) == 0;
442 _vsc_cli_option_list_append (struct VscError *error,
443 struct VscCliOption **option_list,
444 const struct VscCliOption *option)
446 VSC__ASSERT (error != NULL);
447 VSC__ASSERT (! error->occured);
448 VSC__ASSERT (option_list != NULL);
449 VSC__ASSERT (option != NULL);
452 (error, (struct VscList **) option_list,
453 (struct VscList *) option,
454 (VscList_DuplicateFunction) _option_duplicate);
456 if (error->occured) {
457 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
463 vsc_cli_option_list_free (struct VscCliOption **option_list)
465 VSC__ASSERT (option_list != NULL);
467 vsc_list_free ((struct VscList **) option_list,
468 (VscList_FreeFunction) _option_free);
471 struct VscCliOption * /* option_list_duplicate */
472 _vsc_cli_option_list_duplicate (struct VscError *error,
473 const struct VscCliOption *option_list)
475 struct VscCliOption *option_list_duplicate = NULL;
477 VSC__ASSERT (error != NULL);
478 VSC__ASSERT (! error->occured);
480 option_list_duplicate = (struct VscCliOption *)
482 (error, (struct VscList *) option_list,
483 (VscList_DuplicateFunction) _option_duplicate,
484 (VscList_FreeFunction) _option_free);
486 if (error->occured) {
487 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
491 return option_list_duplicate;
494 struct VscCliOption *
495 _vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
496 const char *option_long_name)
498 VSC__ASSERT (option_long_name != NULL);
500 return (struct VscCliOption *)
502 ((struct VscList *) option_list,
503 (VscList_MatchFunction) _option_match,
504 (void *) option_long_name);