Skip to content
Renaud Guillard edited this page Jun 3, 2013 · 8 revisions

C command line parser API

All C type and function names described in this document are presented in their default naming style (lower_case_with_underscorces)

  • [C command line parser API](#C command line parser API)
    • [Generic public API](#Generic public API)
    • [Auto-generated types and functions](#Auto-generated types and functions)
      • Structs
        • [Program interface informations](#Program interface informations)
      • [Parser result](#Parser result)
      • Functions
    • [Code snippets](#Code snippets)
      • [Display program usage](#Display program usage)
      • [Calling the parser](#Calling the parser)
    • [About memory usage](#About memory usage)
    • Tweaking
      • [Identifier naming style](#Identifier naming style)
      • [Code formatting](#Code formatting)
    • [See also](#See also)

Generic public API

Messages

#!cpp
enum nsxml_message_type
{
	nsxml_message_type_debug = 0,
	nsxml_message_type_warning,
	nsxml_message_type_error,
	nsxml_message_type_fatal_error,

	nsxml_message_type_count
};

struct _nsxml_message
{
	int type;
	int code;
	char *message;
	struct _nsxml_message* next_message;
};
typedef struct _nsxml_message nsxml_message;

This struct stores messages raised by the parser during the command line argument parsing process. The code member refers to the message codes defines in the specification compliance page.

Value

#!cpp
enum nsxml_value_type
{
	nsxml_value_type_unset = -1,
	nsxml_value_type_null,
	nsxml_value_type_int,
	nsxml_value_type_float,
	nsxml_value_type_string
};

struct _nsxml_value
{
	int type;
	const char *string_value;
	int int_value;
	float float_value;
	struct _nsxml_value *next_value;
};
typedef struct _nsxml_value nsxml_value;

This struct stores options argument(s) and positional arguments values. The string_value is always filled and points to one of the command line argument item (argv[]) except if type is set to nsxml_value_type_null. Depending on option/positional argument specifications. int_value and float_value will be set by converting the value of string_value

Utilities

Theses elements are used internally by the C parser but can be used for various purpose. See the code for more informations

String copy

#!cpp
int nsxml_util_strncpy(char *output, size_t output_length, const char *input, size_t input_length);
int nsxml_util_strcpy(char *output, size_t output_length, const char *input);
int nsxml_util_asnprintf(char **output, size_t *output_length, const char *format, ...);

Text wrapping

#!cpp
enum nsxml_util_text_indent_mode
{
	nsxml_util_text_wrap_indent_none = 0,/**!< Do not indent */
	nsxml_util_text_wrap_indent_first,   /**!< Indent first line */
	nsxml_util_text_wrap_indent_others   /**!< Indent all line except the first */
};
enum nsxml_util_text_wrap_eol
{
	nsxml_util_text_wrap_eol_cr = 1,
	nsxml_util_text_wrap_eol_lf = 2,
	nsxml_util_text_wrap_eol_crlf = 3
};
void nsxml_util_text_wrap_options_init(struct nsxml_util_text_wrap_options* options, int tab, int line, int indent_mode, int eol);
void nsxml_util_text_wrap_fprintf(FILE *stream, const char *text, const struct nsxml_util_text_wrap_options* options, int level);

Auto-generated types and functions

Here, we assume your_app as the prefix used to generate the program parser.

Structs

Program interface informations

A C representation of the program interface description is generated based on the generic nsxml_*_info structures (internal API)

#!cpp
typedef struct nsxml_program_info your_app_info;
your_app_info *your_app_info_new();
void your_app_info_init(your_app_info *info);

Initialize or create a new C representation of the program interface description.

  • your_app_info_init should be used on manually created your_app_info only.

  • your_app_info_new calls your_app_info_init internally

    #!cpp void your_app_info_cleanup(your_app_info *info); void your_app_info_free(your_app_info *info);

Release internal resources or completely free a your_app_info struct

  • your_app_info_cleanup should be used on manually created your_app_info only.
  • your_app_info_free calls your_app_info_cleanup internally

Parser result

The parsing result is stored in a struct where most of members depends on subcommand and variable names described in the program interface XML definition file. Here is a sample of what you can get.

#!cpp
struct _your_app_result
{
	/* Messages - Sorted by severity */
	nsxml_message *messages[nsxml_message_type_count];
	/* Messages - Sorted by apparition */
	nsxml_message *first_message;
	
	/* Subcommand */
	const char *subcommand_name;
	/* Values */
	int value_count;
	nsxml_value *values;
	/* Global options */
	struct 
	{
		struct nsxml_switch_option_result displayHelp;
		struct nsxml_switch_option_result verboseProgram;
		struct nsxml_argument_option_result uiArg;
		struct nsxml_argument_option_result standardArg;
		/* ... */
	} options;
	
	/* Subcommands */
	struct 
	{
		struct your_app_sub_subcommand_result
		{
			/* Subcommand options */
			struct nsxml_switch_option_result _switch;
		} sub;
		struct your_app_sub_subcommand_result
		{
			/* Subcommand options */
			struct nsxml_argument_option_result subFile;
			struct nsxml_argument_option_result subEnum;
		} foo;
	} subcommands;
};
typedef struct _your_app_result your_app_result;		
  • result->subcommand_name points to the name of the selected subcommand (if any, otherwise NULL)
  • result->values is a list of (valid) positional arguments found on the command line

All nsxml_*_option_result structs have a is_set member. This member is set to 1 if the option is present and valid.

  • nsxml_argument_option_result adds a nsxml_value argument that store the option argument (if option is set)
  • nsxml_multiargument_option_result adds a nsxml_value arguments that store the option argument(s) (if option is set)
  • nsxml_group_option_result adds nsxml_option_result *selected_option and const char *selected_option_name set if the group is an exclusive group and a sub option of this group is set. The selected_option_name refers to the variable name of the sub option and selected_option points to the option's nsxml_*_option_result

Functions

#!cpp
your_app_result *your_app_parse(your_app_info *info, int argc, const char **argv, int start_index);

Parse the command line arguments (given by argc and argv), starting with the start_indexth argument. The function returns a new your_app_result* described above

#!cpp
void your_app_usage(FILE *stream, your_app_info *info, your_app_result *result, int format, const struct nsxml_util_text_wrap_options *wrap);

Print program usage. If result is given, the function will use the result state to display contextual help (depending if a subcommand was set or not)

#!cpp 
int your_app_result_error_count(your_app_result *result);

Return the number of errors (+ the fatal error if any) raised during the command line parsing

#!cpp
nsxml_message *your_app_result_get_warnings(your_app_result *result);
nsxml_message *your_app_result_get_errors(your_app_result *result);
nsxml_message *your_app_result_get_fatal_error(your_app_result *result);

Shortcuts to access your_app_result->messages[nsxml_message_type_*]

#!cpp
void your_app_result_display_errors(FILE *stream, your_app_result *result, const char *line_prefix);

Display errors (and fatal error) on a given output, prefixing all lines with line_prefix

#!cpp
void your_app_result_free(your_app_result *result);

Free a your_app_result allocated by a call to your_app_parse()

Code snippets

Display program usage

#!cpp
your_app_info info;
your_app_info_init(&info);
your_app_usage(stdout, &info, NULL, nsxml_usage_format_short, NULL);
your_app_info_cleanup(&info);

Or ...

#!cpp
your_app_info *info = your_app_info_new();
your_app_usage(stdout, info, NULL, nsxml_usage_format_short, NULL);
your_app_info_free(info);

Calling the parser

#!cpp
your_app_info *info = your_app_info_new();
your_app_result *result = your_app_parse(info, argc, argv, 1);
if (your_app_result_error_count(result))
{
	your_app_result_display_errors(stderr, result, " - ");
	your_app_result_free(result);
	your_app_info_free(info);
	return EXIT_FAILURE; 
}

/* Dealing with a switch */
if (result.options.displayHelp.is_set)
{
	your_app_usage(stdout, info, NULL, nsxml_usage_format_details, NULL);
	your_app_result_free(result);
	your_app_info_free(info);
	return EXIT_SUCCESS;
}

/* .. with a single-argument option */
if (result.options.arg.is_set)
{
	printf("Value of the single-argument option: %s\n", result.options.arg.argument.string_value);
}

/* ... with a multi-argument option */
if (result.options.multi.is_set)
{
	nsxml_value *v = result.options.multi.arguments;
	int i = 0;
	while (v)
	{
		printf("Value of argument %d of multi option: %s\n", i, v->string_value);
		v = v->next_value;
		++i;
	}
}

your_app_result_free(result);
your_app_info_free(info);
return EXIT_SUCCESS;

About memory usage

Most of all elements of the your_app_result will be valid until a call to your_app_result_free() or your_app_cleanup(). However some string values refers to data allocated by foreign objects.

  • nsxml_value->string_value points to one of the elememts of argv{} (the argument given to your_app_parse())
  • result->sub_command_name points to a string allocated in your_app_info
  • greup_result->selected_option_name points to a string allocated in your_app_info

So releasing or modifying your_app_info or argc/argv[] is not recommended before a call to your_app_result_free() or your_app_cleanup().

Tweaking

Identifier naming style

The general naming style of the C parser source code is lower_case_with_underscorces. Naming style of the public API and auto-generated structs, functions and enums can be changed to CamelCase or camelCase using [build-c.sh](https://github.com/noresources/ns-xml/wiki/build-c) tool

#!bash
build-c.sh -x myapp.xml --struct CamelCase --function camelCase -o out 
  • your_app_info will be changed to YourAppInfo
  • your_app_info_init() will be change to yourAppInfoInit()

Variable names given in the program interface XML description file (<prg:databinding><prg:variable>my_option</prg:variable></prg:databinding>) and internal API structs and functions are not modified.

Code formatting

There is no parameters to change the default code formatting of the generated files. You may post process the generated files using a third party tool such as Artistic Style

See also


The program interface definition framework