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->tagged) {
43 if (option_info->type == VSC_CLI__OPTION_TYPE__BOOLEAN) {
45 if (option_info->short_name > 0) {
46 snprintf (syntax, 512, "-%c", option_info->short_name);
48 snprintf (syntax, 512, "--%s", option_info->long_name);
51 if (option_info->short_name > 0) {
52 snprintf (syntax, 512, "-%c, --%s", option_info->short_name,
53 option_info->long_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 if (option_info->short_name > 0) {
79 snprintf (syntax, 512, "-%c %s", option_info->short_name,
82 snprintf (syntax, 512, "--%s %s", option_info->long_name,
86 if (option_info->short_name > 0) {
87 snprintf (syntax, 512, "-%c %s, --%s %s",
88 option_info->short_name, type,
89 option_info->long_name, type);
91 snprintf (syntax, 512, "--%s %s", option_info->long_name,
97 snprintf (syntax, 512, "<%s>", option_info->long_name);
103 int /* option_parsed */
104 _vsc_cli_option_parse (struct VscError *error,
105 const struct VscCliOptionInfo *option_infos,
106 int untagged_option_index, const char *command_name,
107 const char *input, const char **input_tail,
108 struct VscCliOption *option)
110 int option_parsed = TRUE;
111 const struct VscCliOptionInfo *option_info;
112 const char *tail = NULL;
116 VSC__ASSERT (error != NULL);
117 VSC__ASSERT (! error->occured);
118 VSC__ASSERT (option_infos != NULL);
119 VSC__ASSERT (input != NULL);
120 VSC__ASSERT (input_tail != NULL);
121 VSC__ASSERT (option != NULL);
125 _vsc_cli_token_parse (error, input, &token, &tail);
127 if (error->occured) {
128 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
136 memset (option, 0, sizeof (struct VscCliOption));
141 if (*(token + 1) == '-') {
146 if (num_dashes > 0) {
147 if (num_dashes == 1 && *(token + 2) != '\0') {
148 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
149 "Invalid option syntax: %s", token);
153 option_info = _vsc_cli_option_infos_lookup_tagged
154 (option_infos, token + num_dashes, num_dashes > 1);
156 if (option_info == NULL) {
157 if (command_name != NULL) {
158 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
159 "Unknown option %s for command '%s'",
160 token, command_name);
162 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
163 "Unknown option %s", token);
169 option->info = option_info;
171 switch (option_info->type) {
172 case VSC_CLI__OPTION_TYPE__BOOLEAN:
175 case VSC_CLI__OPTION_TYPE__NUMBER:
177 _vsc_cli_token_parse (error, tail, &token, &tail);
179 if (error->occured) {
180 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
185 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
186 "Expected option syntax: %s",
187 _vsc_cli_option_syntax (option_info, FALSE));
191 option->number = vsc_strtoi (error, token, NULL, 10);
193 if (error->occured) {
194 vsc_error_cleanup (error);
195 vsc_error_init (error);
197 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
198 "Expected option syntax: %s",
199 _vsc_cli_option_syntax (option_info, FALSE));
205 case VSC_CLI__OPTION_TYPE__STRING:
207 _vsc_cli_token_parse (error, tail, &token, &tail);
209 if (error->occured) {
210 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
215 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
216 "Expected option syntax: %s",
217 _vsc_cli_option_syntax (option_info, FALSE));
221 option->string = token;
226 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
231 if (error->occured) {
232 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
235 } else if (untagged_option_index >= 0) {
236 option_info = _vsc_cli_option_infos_lookup_untagged
237 (option_infos, untagged_option_index);
239 if (option_info == NULL) {
240 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
241 "Too many options for command '%s'",
246 option->info = option_info;
247 option->string = token;
250 if (error->occured) {
251 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
255 option_parsed = FALSE;
265 return option_parsed;
268 vsc_free (&option->string);
270 option_parsed = FALSE;
275 static const struct VscCliOption *
276 _option_get (const struct VscCliOption *option_list,
277 const char *option_long_name)
279 const struct VscCliOption *option;
281 VSC__ASSERT (option_long_name != NULL);
283 for (option = option_list; option != NULL; option = option->next) {
284 if (strcmp (option->info->long_name, option_long_name) == 0) {
293 vsc_cli_option_get_boolean (const struct VscCliOption *option_list,
294 const char *option_long_name)
296 return _option_get (option_list, option_long_name) != NULL;
300 vsc_cli_option_get_number (const struct VscCliOption *option_list,
301 const char *option_long_name, int *found)
303 const struct VscCliOption *option;
305 VSC__ASSERT (option_long_name != NULL);
311 option = _option_get (option_list, option_long_name);
313 if (option == NULL) {
321 return option->number;
325 vsc_cli_option_get_string (const struct VscCliOption *option_list,
326 const char *option_long_name)
328 const struct VscCliOption *option;
330 VSC__ASSERT (option_long_name != NULL);
332 option = _option_get (option_list, option_long_name);
334 if (option == NULL) {
338 return option->string;
341 const struct VscCliOptionInfo *
342 _vsc_cli_option_infos_lookup_tagged
343 (const struct VscCliOptionInfo *option_infos, const char *option_name,
348 VSC__ASSERT (option_infos != NULL);
349 VSC__ASSERT (option_name != NULL);
351 for (i = 0; option_infos[i].long_name != NULL; i++) {
352 if (! option_infos[i].tagged) {
357 if (strcmp (option_name, option_infos[i].long_name) == 0) {
358 return &option_infos[i];
361 if (*option_name == option_infos[i].short_name) {
362 return &option_infos[i];
370 const struct VscCliOptionInfo *
371 _vsc_cli_option_infos_lookup_untagged
372 (const struct VscCliOptionInfo *option_infos, int position)
376 VSC__ASSERT (option_infos != NULL);
377 VSC__ASSERT (position >= 0);
379 for (i = 0; option_infos[i].long_name != NULL; i++) {
380 if (! option_infos[i].tagged) {
384 return &option_infos[i];
393 _option_free (struct VscCliOption **option)
395 VSC__ASSERT (option != NULL);
397 if (*option != NULL) {
398 vsc_free (&(*option)->string);
404 static struct VscCliOption * /* option_duplicate */
405 _option_duplicate (struct VscError *error, const struct VscCliOption *option)
407 struct VscCliOption *option_duplicate = NULL;
409 VSC__ASSERT (error != NULL);
410 VSC__ASSERT (! error->occured);
411 VSC__ASSERT (option != NULL);
413 option_duplicate = vsc_memdup (error, option, sizeof (struct VscCliOption));
415 if (error->occured) {
416 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
420 if (option->string != NULL) {
421 option_duplicate->string = vsc_strdup (error, option->string);
424 if (error->occured) {
425 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
429 option_duplicate->next = NULL;
431 return option_duplicate;
434 _option_free (&option_duplicate);
440 _option_match (struct VscList *item, void *match_data)
442 struct VscCliOption *option = (struct VscCliOption *) item;
443 const char *option_long_name = (const char *) match_data;
445 VSC__ASSERT (item != NULL);
447 return strcmp (option->info->long_name, option_long_name) == 0;
451 _vsc_cli_option_list_append (struct VscError *error,
452 struct VscCliOption **option_list,
453 const struct VscCliOption *option)
455 VSC__ASSERT (error != NULL);
456 VSC__ASSERT (! error->occured);
457 VSC__ASSERT (option_list != NULL);
458 VSC__ASSERT (option != NULL);
461 (error, (struct VscList **) option_list,
462 (struct VscList *) option,
463 (VscList_DuplicateFunction) _option_duplicate);
465 if (error->occured) {
466 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
472 vsc_cli_option_list_free (struct VscCliOption **option_list)
474 VSC__ASSERT (option_list != NULL);
476 vsc_list_free ((struct VscList **) option_list,
477 (VscList_FreeFunction) _option_free);
480 struct VscCliOption * /* option_list_duplicate */
481 _vsc_cli_option_list_duplicate (struct VscError *error,
482 const struct VscCliOption *option_list)
484 struct VscCliOption *option_list_duplicate = NULL;
486 VSC__ASSERT (error != NULL);
487 VSC__ASSERT (! error->occured);
489 option_list_duplicate = (struct VscCliOption *)
491 (error, (struct VscList *) option_list,
492 (VscList_DuplicateFunction) _option_duplicate,
493 (VscList_FreeFunction) _option_free);
495 if (error->occured) {
496 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
500 return option_list_duplicate;
503 struct VscCliOption *
504 _vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
505 const char *option_long_name)
507 VSC__ASSERT (option_long_name != NULL);
509 return (struct VscCliOption *)
511 ((struct VscList *) option_list,
512 (VscList_MatchFunction) _option_match,
513 (void *) option_long_name);