Command Line Argument Parser for C
[
](LICENSE)
[
]() 
libclap is a modern, secure, and extensible command-line argument parsing library for C, inspired by Python's argparse. It provides a familiar, feature-rich API while maintaining C99 compatibility, zero external dependencies, and a strong focus on memory safety.
✨ Features
- Full argparse Compatibility – Positional arguments, optional flags, subcommands, mutually exclusive groups,
nargs (*, +, ?, N, REMAINDER), and more.
- Rich Action System –
store, store_true/false, store_const, append, append_const, count, help, version, and custom actions.
- Type Conversion – Built-in support for
int, float, string, and bool, plus user‑defined converters.
- Automatic Help Generation – Beautifully formatted usage and help messages with smart line‑wrapping and alignment.
- Subcommands – Full support for git‑style sub‑parsers with flat namespace merging.
- Memory Safe – Bounds‑checked string buffers, input validation, and comprehensive leak testing (Valgrind/ASan clean).
- Cross‑Platform – Windows, Linux, macOS, and BSD. Works with GCC, Clang, and MSVC.
- Zero Dependencies – Only requires a C99 standard library.
- CMake Integration – Easy to build and consume with
find_package(clap) or pkg-config.
📦 Installation
From Source
git clone https://github.com/wdl0214/libclap.git
cd libclap
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local
make
sudo make install
CMake Options
| Option | Default | Description |
CLAP_BUILD_SHARED | ON | Build shared library |
CLAP_BUILD_STATIC | ON | Build static library |
CLAP_BUILD_TESTS | ON | Build unit tests |
CLAP_BUILD_EXAMPLES | ON | Build example programs |
CLAP_BUILD_FUZZ | OFF | Build fuzz test (Clang only) |
CLAP_ENABLE_ASAN | OFF | Enable AddressSanitizer |
CLAP_ENABLE_UBSAN | OFF | Enable UndefinedBehaviorSanitizer |
CLAP_ENABLE_COVERAGE | OFF | Enable code coverage |
Using in Your Project
⚠️ Static linking: If you link libclap statically, you must define CLAP_STATIC at compile time (e.g. -DCLAP_STATIC or #define CLAP_STATIC). This is required on all platforms, but most important on Windows where the absence of CLAP_STATIC causes the compiler to emit __declspec(dllimport) decorations that are incompatible with a static library.
When linking against the shared library, no additional defines are needed.
CMake <tt>find_package</tt>
find_package(clap REQUIRED)
add_executable(myapp myapp.c)
target_link_libraries(myapp PRIVATE clap::clap_static)
target_compile_definitions(myapp PRIVATE CLAP_STATIC)
Use clap::clap_shared instead if you prefer linking against the shared library (no CLAP_STATIC needed).
pkg-config
# Dynamic linking (default)
gcc myapp.c -o myapp $(pkg-config --cflags --libs libclap)
# Static linking — requires -DCLAP_STATIC
gcc myapp.c -o myapp $(pkg-config --cflags --static --libs libclap) -DCLAP_STATIC
Manual Linking
# Dynamic linking
gcc myapp.c -o myapp -I/usr/local/include -L/usr/local/lib -lclap
# Static linking — requires -DCLAP_STATIC
gcc myapp.c -o myapp -I/usr/local/include /usr/local/lib/libclap.a -DCLAP_STATIC
🚀 Quick Start
#include <stdio.h>
int main(int argc, char *argv[]) {
return 1;
}
return 0;
}
const char *input, *output;
int verbose = 0;
printf("Input: %s, Output: %s, Verbose: %d\n", input, output, verbose);
return 0;
}
Main public API for libclap.
CLAP_EXPORT void clap_parser_free(clap_parser_t *parser)
Destroy a parser and all resources it owns.
CLAP_EXPORT clap_parse_result_t clap_parse_args(clap_parser_t *parser, int argc, char *argv[], clap_namespace_t **out_namespace, clap_error_t *error)
Parse command-line arguments.
CLAP_EXPORT clap_argument_t * clap_argument_default(clap_argument_t *arg, const char *default_value)
Set a default value for an argument.
CLAP_EXPORT void clap_print_help_on_error(clap_parser_t *parser, const clap_error_t *error, FILE *stream)
Print "<prog>: error: <message>" and contextual help.
CLAP_EXPORT clap_argument_t * clap_argument_type(clap_argument_t *arg, const char *type_name)
Set the type name for an argument.
CLAP_EXPORT clap_argument_t * clap_argument_help(clap_argument_t *arg, const char *help_text)
Set the help text for an argument.
CLAP_EXPORT void clap_namespace_free(clap_namespace_t *ns)
Free a namespace and all values it contains. NULL-safe.
CLAP_EXPORT bool clap_namespace_get_string(clap_namespace_t *ns, const char *name, const char **value)
Retrieve a string value from the namespace.
CLAP_EXPORT clap_argument_t * clap_argument_required(clap_argument_t *arg, bool required)
Mark an optional argument as required or not.
CLAP_EXPORT bool clap_namespace_get_int(clap_namespace_t *ns, const char *name, int *value)
Retrieve an int value from the namespace.
CLAP_EXPORT clap_argument_t * clap_argument_action(clap_argument_t *arg, clap_action_t action)
Set the action type for an argument.
CLAP_EXPORT clap_argument_t * clap_add_argument(clap_parser_t *parser, const char *name_or_flags)
Register a new argument (positional or optional).
CLAP_EXPORT clap_parser_t * clap_parser_new(const char *prog_name, const char *description, const char *epilog)
Create a new argument parser.
@ CLAP_ACTION_COUNT
Definition clap_action.h:23
clap_parse_result_t
Parse result codes returned by clap_parse_args()
Definition clap_types.h:60
@ CLAP_PARSE_HELP
Definition clap_types.h:63
@ CLAP_PARSE_ERROR
Definition clap_types.h:62
@ CLAP_PARSE_VERSION
Definition clap_types.h:64
Argument definition (positional or optional).
Error information structure.
Definition clap_error.h:36
Container for parsed argument values.
Parsing engine state and configuration.
Compile and run:
$ gcc -o myapp myapp.c -lclap
$ ./myapp --help
Usage: my_program [-h] [--output OUTPUT] [--verbose] input
A simple example
Positional arguments:
input Input file
Optional arguments:
--help, -h Show this help message and exit
--output, -o OUTPUT Output file (default: -)
--verbose, -v Increase verbosity
📚 API Overview
Parser Lifecycle
return 1;
}
return 0;
}
CLAP_EXPORT void clap_parser_set_help_width(clap_parser_t *parser, int width)
Set help output width (default: 100).
CLAP_EXPORT void clap_parser_set_version(clap_parser_t *parser, const char *version)
Set the version string printed by clap_print_version().
Adding Arguments
CLAP_EXPORT clap_argument_t * clap_argument_metavar(clap_argument_t *arg, const char *metavar)
Override the metavar shown in help/usage for this argument.
Actions
| Action | Description |
CLAP_ACTION_STORE | Store the value (default) |
CLAP_ACTION_STORE_TRUE | Store true when flag is present |
CLAP_ACTION_STORE_FALSE | Store false when flag is present |
CLAP_ACTION_STORE_CONST | Store a constant value |
CLAP_ACTION_APPEND | Append value to a list |
CLAP_ACTION_APPEND_CONST | Append constant to a list |
CLAP_ACTION_COUNT | Count occurrences of the option |
CLAP_ACTION_HELP | Print help and exit |
CLAP_ACTION_VERSION | Print version and exit |
CLAP_ACTION_CUSTOM | User‑defined handler |
nargs Specifiers
| Specifier | Description |
1 (default) | Exactly one argument |
? (CLAP_NARGS_ZERO_OR_ONE) | Zero or one argument |
* (CLAP_NARGS_ZERO_OR_MORE) | Zero or more arguments |
+ (CLAP_NARGS_ONE_OR_MORE) | One or more arguments |
N | Exactly N arguments |
REMAINDER | Consume all remaining arguments |
Subcommands
const char *cmd;
if (strcmp(cmd, "commit") == 0) {
const char *msg;
}
CLAP_EXPORT clap_parser_t * clap_subparser_add(clap_parser_t *subparsers, const char *name, const char *help_text)
Register a subcommand.
CLAP_EXPORT clap_argument_t * clap_argument_dest(clap_argument_t *arg, const char *dest)
Override the destination key (dest) in the namespace.
CLAP_EXPORT clap_parser_t * clap_add_subparsers(clap_parser_t *parser, const char *dest, const char *help_text)
Enable subcommand support.
Mutually Exclusive Groups
CLAP_EXPORT int clap_add_mutually_exclusive_group(clap_parser_t *parser, bool required)
Create a mutually exclusive group.
CLAP_EXPORT bool clap_mutex_group_add_argument(clap_parser_t *parser, int mutex_group_id, clap_argument_t *arg)
Add an argument to a mutually exclusive group.
@ CLAP_ACTION_STORE_TRUE
Definition clap_action.h:19
Argument Groups
Organize related arguments into named sections in the help output:
CLAP_EXPORT int clap_add_argument_group(clap_parser_t *parser, const char *title, const char *description)
Create a display group to organize arguments in help output.
CLAP_EXPORT bool clap_argument_group_add_argument(clap_parser_t *parser, int display_group_id, clap_argument_t *arg)
Add an argument to a display group.
Help output:
Usage: prog [-h] [--host HOST] [--port PORT]
Optional arguments:
-h, --help Show this help message and exit
Network:
Connection options
--host HOST Hostname to connect to
--port PORT Port number
Arguments in a display group are excluded from the default "Optional arguments" and "Positional arguments" sections, appearing only in their group section.
Custom Memory Allocator
void* my_malloc(size_t size) { return pool_alloc(size); }
void my_free(void *ptr) { pool_free(ptr); }
int main() {
}
CLAP_EXPORT void clap_set_allocator(void *(*malloc_fn)(size_t), void(*free_fn)(void *), void *(*realloc_fn)(void *, size_t))
Set custom memory allocator functions.
Frequently Asked Questions
How do I handle CLAP_PARSE_HELP / CLAP_PARSE_VERSION?
These are not errors. The library has already printed the help/version text to stdout. The caller should clean up and exit successfully:
Why does my custom type handler return "Unknown type"?
You registered a type name via clap_argument_type(arg, "ip_addr") but forgot to register the converter with clap_register_type(parser, "ip_addr", my_handler, sizeof(my_ip_t)). The type handler must be registered on the parser before parsing begins.
What is CLAP_STATIC and when do I need it?
When linking libclap statically (e.g. libclap.a on Unix or clap.lib on Windows), you must define CLAP_STATIC at compile time (-DCLAP_STATIC or #define CLAP_STATIC). This is required because libclap's headers use __declspec(dllimport) on Windows by default, which is incompatible with a static library. Shared library users do not need this define.
My –verbose/-v flag doesn't consume a value, but the next argument is skipped
This is expected. Flags using CLAP_ACTION_STORE_TRUE, STORE_FALSE, STORE_CONST, APPEND_CONST, or COUNT do not consume a value. If you need a flag that takes a value, use CLAP_ACTION_STORE (the default) with clap_argument_type().
Why is my subcommand's help not showing the right name?
Subcommand names are extracted from the parser's prog_name. When creating a subparser with clap_subparser_add(subparsers, "commit", ...), the subparser's prog_name is set to "parent_prog commit". The display logic extracts the portion after the last space, so names with spaces (e.g. "myprog nested sub") will only show "sub".
How do I make a positional argument optional?
Use ‘clap_argument_nargs(arg, ’?')(zero or one),'*'(zero or more), or setCLAP_NARGS_REMAINDER`. By default, positional arguments are required.
Testing
libclap includes a comprehensive test suite built with Unity.
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug
make
ctest # Run all tests
ctest -R usage # Run only usage compliance tests
ctest --output-on-failure # Show output for failed tests
./tests/test_parser # Run a specific test
Memory safety is verified with deterministic runtime checks and sanitizers:
valgrind --leak-check=full ./tests/test_usage
cmake .. -DCLAP_ENABLE_ASAN=ON && make && ctest
The Valgrind command above runs the test_usage executable under a leak checker. It is most useful on Linux; for regular development and CI, ASan is usually faster and easier to integrate.
Code coverage requires gcovr to be installed:
pip install gcovr
cmake .. -DCLAP_ENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug
make && ctest
make coverage
Fuzz testing is available with Clang builds:
mkdir build && cd build
cmake .. -DCLAP_BUILD_FUZZ=ON -DCMAKE_C_COMPILER=clang
make fuzz_clap
./tests/fuzz_clap -max_len=4096 -runs=100000 corpus/
Unlike the fixed test suite, fuzzing continuously generates and mutates inputs to find crashes, unexpected exits, leaks, and other parser edge cases.
📖 Examples
| Example | Description |
examples/basic.c | Minimal argument parsing |
examples/git_style.c | Full git‑style CLI with subcommands |
examples/action_demo.c | Demonstrates all 10 action types |
examples/custom_type.c | Demonstrates custom type validation and typed storage with CLAP_ACTION_CUSTOM |
🏗️ Project Structure
libclap/
├── CMakeLists.txt
├── cmake/ # CMake modules
├── include/clap/ # Public headers
├── src/ # Library source
├── tests/ # Unit and integration tests
│ ├── unit/ # Per-module unit tests
│ ├── integration/ # End-to-end tests
│ └── fuzz/ # Fuzz testing
├── examples/ # Example programs
└── scripts/ # Test and utility scripts
🤝 Contributing
Contributions are welcome! Please open an issue or pull request on GitHub. Ensure that:
- Code follows the existing C99 style
- All tests pass (
ctest)
- No new compiler warnings are introduced
- Valgrind/ASan reports no leaks
- Parser and tokenizer changes are validated with fuzzing when possible
Changelog
See CHANGELOG.md for version history and release notes.
License
libclap is released under the MIT License. See [LICENSE](LICENSE) for details.
libclap – argparse for C, without compromise.