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_error_cleanup (error);
187 vsc_error_init (error);
189 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
190 "Expected option syntax: %s",
191 _vsc_cli_option_syntax (option->info, FALSE));
197 case VSC_CLI__OPTION_TYPE__STRING:
199 _vsc_cli_token_parse (error, tail, &token, &tail);
201 if (error->occured) {
202 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
207 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
208 "Expected option syntax: %s",
209 _vsc_cli_option_syntax (option->info, FALSE));
213 option->string = token;
218 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
223 if (error->occured) {
224 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
227 } else if (untagged_option_index >= 0) {
228 option->info = _vsc_cli_option_infos_lookup_untagged
229 (option_infos, untagged_option_index);
231 if (option->info == NULL) {
232 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
233 "Too many untagged options for command '%s'",
238 switch (option->info->type) {
239 case VSC_CLI__OPTION_TYPE__BOOLEAN:
240 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
241 "Boolean options can not untagged");
245 case VSC_CLI__OPTION_TYPE__NUMBER:
246 option->number = vsc_strtoi (error, token, NULL, 10);
248 if (error->occured) {
249 vsc_error_cleanup (error);
250 vsc_error_init (error);
252 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
253 "Expected option syntax: %s",
254 _vsc_cli_option_syntax (option->info, FALSE));
260 case VSC_CLI__OPTION_TYPE__STRING:
261 option->string = token;
266 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
271 if (error->occured) {
272 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
276 option_parsed = FALSE;
286 return option_parsed;
289 vsc_free (&option->string);
290 memset (option, 0, sizeof (struct VscCliOption));
292 option_parsed = FALSE;
297 const struct VscCliOption *
298 vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
299 const char *option_long_name)
301 const struct VscCliOption *option;
303 VSC__ASSERT (option_long_name != NULL);
305 for (option = option_list; option != NULL; option = option->next) {
306 if (strcmp (option->info->long_name, option_long_name) == 0) {
315 vsc_cli_option_list_lookup_boolean (const struct VscCliOption *option_list,
316 const char *option_long_name)
318 const struct VscCliOption *option;
320 option = vsc_cli_option_list_lookup (option_list, option_long_name);
322 if (option != NULL) {
323 VSC__ASSERT (option->info->type == VSC_CLI__OPTION_TYPE__BOOLEAN);
332 vsc_cli_option_list_lookup_number (const struct VscCliOption *option_list,
333 const char *option_long_name, int *found)
335 const struct VscCliOption *option;
337 VSC__ASSERT (option_long_name != NULL);
343 option = vsc_cli_option_list_lookup (option_list, option_long_name);
345 if (option == NULL) {
349 VSC__ASSERT (option->info->type == VSC_CLI__OPTION_TYPE__NUMBER);
355 return option->number;
359 vsc_cli_option_list_lookup_string (const struct VscCliOption *option_list,
360 const char *option_long_name)
362 const struct VscCliOption *option;
364 VSC__ASSERT (option_long_name != NULL);
366 option = vsc_cli_option_list_lookup (option_list, option_long_name);
368 if (option == NULL) {
372 VSC__ASSERT (option->info->type == VSC_CLI__OPTION_TYPE__STRING);
374 return option->string;
377 const struct VscCliOptionInfo *
378 _vsc_cli_option_infos_lookup_tagged
379 (const struct VscCliOptionInfo *option_infos, const char *option_name,
384 VSC__ASSERT (option_infos != NULL);
385 VSC__ASSERT (option_name != NULL);
387 for (i = 0; option_infos[i].long_name != NULL; i++) {
388 if (! (option_infos[i].flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
393 if (strcmp (option_name, option_infos[i].long_name) == 0) {
394 return &option_infos[i];
397 if (*option_name == option_infos[i].short_name) {
398 return &option_infos[i];
406 const struct VscCliOptionInfo *
407 _vsc_cli_option_infos_lookup_untagged
408 (const struct VscCliOptionInfo *option_infos, int position)
412 VSC__ASSERT (option_infos != NULL);
413 VSC__ASSERT (position >= 0);
415 for (i = 0; option_infos[i].long_name != NULL; i++) {
416 if (! (option_infos[i].flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
420 return &option_infos[i];
429 _option_free (struct VscCliOption **option)
431 VSC__ASSERT (option != NULL);
433 if (*option != NULL) {
434 vsc_free (&(*option)->string);
440 static struct VscCliOption * /* option_duplicate */
441 _option_duplicate (struct VscError *error, const struct VscCliOption *option)
443 struct VscCliOption *option_duplicate = NULL;
445 VSC__ASSERT (error != NULL);
446 VSC__ASSERT (! error->occured);
447 VSC__ASSERT (option != NULL);
449 option_duplicate = vsc_memdup (error, option, sizeof (struct VscCliOption));
451 if (error->occured) {
452 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
456 if (option->string != NULL) {
457 option_duplicate->string = vsc_strdup (error, option->string);
460 if (error->occured) {
461 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
465 option_duplicate->next = NULL;
467 return option_duplicate;
470 _option_free (&option_duplicate);
476 _option_match (struct VscList *item, void *match_data)
478 struct VscCliOption *option = (struct VscCliOption *) item;
479 const char *option_long_name = (const char *) match_data;
481 VSC__ASSERT (item != NULL);
483 return strcmp (option->info->long_name, option_long_name) == 0;
487 _vsc_cli_option_list_append (struct VscError *error,
488 struct VscCliOption **option_list,
489 const struct VscCliOption *option)
491 VSC__ASSERT (error != NULL);
492 VSC__ASSERT (! error->occured);
493 VSC__ASSERT (option_list != NULL);
494 VSC__ASSERT (option != NULL);
497 (error, (struct VscList **) option_list,
498 (struct VscList *) option,
499 (VscList_DuplicateFunction) _option_duplicate);
501 if (error->occured) {
502 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
508 vsc_cli_option_list_free (struct VscCliOption **option_list)
510 VSC__ASSERT (option_list != NULL);
512 vsc_list_free ((struct VscList **) option_list,
513 (VscList_FreeFunction) _option_free);
516 struct VscCliOption * /* option_list_duplicate */
517 _vsc_cli_option_list_duplicate (struct VscError *error,
518 const struct VscCliOption *option_list)
520 struct VscCliOption *option_list_duplicate = NULL;
522 VSC__ASSERT (error != NULL);
523 VSC__ASSERT (! error->occured);
525 option_list_duplicate = (struct VscCliOption *)
527 (error, (struct VscList *) option_list,
528 (VscList_DuplicateFunction) _option_duplicate,
529 (VscList_FreeFunction) _option_free);
531 if (error->occured) {
532 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
536 return option_list_duplicate;
539 struct VscCliOption *
540 _vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
541 const char *option_long_name)
543 VSC__ASSERT (option_long_name != NULL);
545 return (struct VscCliOption *)
547 ((struct VscList *) option_list,
548 (VscList_MatchFunction) _option_match,
549 (void *) option_long_name);