Subcommands
cargs supports Git/Docker-style subcommands, allowing you to create command-line interfaces with distinct commands.
Overview
Subcommands let you organize your program's functionality into separate commands, each with its own options and behavior. Examples include:
git commit,git push,git pulldocker run,docker build,docker pull
This guide covers the basics of implementing subcommands in cargs. For advanced usage including nested subcommands, see the Nested Commands guide.
Basic Concept
Subcommands transform your program from a simple utility into a multi-purpose tool with specialized commands:
Each command can have its own set of options and behavior while sharing common global options.
Implementing Subcommands
To implement subcommands in cargs, you need to:
- Define options for each subcommand
- Define actions for each subcommand
- Reference the subcommands in your main options
1. Define Subcommand Options
First, define the options for each of your subcommands:
// Options for the "add" subcommand
CARGS_OPTIONS(
add_options,
HELP_OPTION(FLAGS(FLAG_EXIT)),
OPTION_FLAG('f', "force", HELP("Force add operation")),
POSITIONAL_STRING("file", HELP("File to add"))
)
// Options for the "remove" subcommand
CARGS_OPTIONS(
remove_options,
HELP_OPTION(FLAGS(FLAG_EXIT)),
OPTION_FLAG('r', "recursive", HELP("Recursively remove directories")),
POSITIONAL_STRING("file", HELP("File to remove"))
)
2. Define Action Functions
Next, define action functions that will be called when each subcommand is executed:
// Action for the "add" subcommand
int add_command(cargs_t *cargs, void *data)
{
// Access subcommand options
const char* file = cargs_get(*cargs, "add.file").as_string;
bool force = cargs_get(*cargs, "add.force").as_bool;
printf("Adding file: %s\n", file);
if (force) printf(" with force option\n");
return 0;
}
// Action for the "remove" subcommand
int remove_command(cargs_t *cargs, void *data)
{
// Access subcommand options
const char* file = cargs_get(*cargs, "remove.file").as_string;
bool recursive = cargs_get(*cargs, "remove.recursive").as_bool;
printf("Removing file: %s\n", file);
if (recursive) printf(" recursively\n");
return 0;
}
3. Define Main Options with Subcommands
Finally, define your main options and include the subcommands:
// Main options with subcommands
CARGS_OPTIONS(
options,
HELP_OPTION(FLAGS(FLAG_EXIT)),
VERSION_OPTION(FLAGS(FLAG_EXIT)),
// Global option applicable to all subcommands
OPTION_FLAG('v', "verbose", HELP("Enable verbose output")),
// Define subcommands
SUBCOMMAND("add", add_options,
HELP("Add files to the index"),
ACTION(add_command)),
SUBCOMMAND("rm", remove_options,
HELP("Remove files from the index"),
ACTION(remove_command))
)
Processing Subcommands
In your main function, you need to check if a subcommand was specified and execute its action:
int main(int argc, char **argv)
{
cargs_t cargs = cargs_init(options, "subcommands_example", "1.0.0");
cargs.description = "Example of subcommands";
int status = cargs_parse(&cargs, argc, argv);
if (status != CARGS_SUCCESS) {
return status;
}
// Check if a subcommand was specified
if (cargs_has_command(cargs)) {
// Execute the subcommand's action
status = cargs_exec(&cargs, NULL);
} else {
printf("No command specified. Use --help to see available commands.\n");
}
cargs_free(&cargs);
return status;
}
Accessing Options
Accessing Subcommand Options
Within a subcommand action function, you can access options in two ways:
Accessing Global Options
Global options are accessible from within subcommand actions:
The leading dot (.) indicates the option is defined at the root level.
Help Integration
cargs automatically integrates subcommands into the help display:
subcommands_example v1.0.0
Example of subcommands
Usage: subcommands_example [OPTIONS] COMMAND
Options:
-h, --help - Display this help message (exit)
-V, --version - Display version information (exit)
-v, --verbose - Enable verbose output
Commands:
add - Add files to the index
rm - Remove files from the index
Run 'subcommands_example COMMAND --help' for more information on a command.
Each subcommand also has its own help:
subcommands_example v1.0.0
Usage: subcommands_example add [OPTIONS] <file>
Add files to the index
Arguments:
<file> - File to add
Options:
-h, --help - Display this help message (exit)
-f, --force - Force add operation
Complete Example
Here's a complete example of implementing basic subcommands:
#include "cargs.h"
#include <stdio.h>
#include <stdlib.h>
// Subcommand action handlers
int add_command(cargs_t *cargs, void *data);
int remove_command(cargs_t *cargs, void *data);
// Define options for "add" subcommand
CARGS_OPTIONS(
add_options,
HELP_OPTION(FLAGS(FLAG_EXIT)),
OPTION_FLAG('f', "force", HELP("Force add operation")),
POSITIONAL_STRING("file", HELP("File to add"))
)
// Define options for "remove" subcommand
CARGS_OPTIONS(
remove_options,
HELP_OPTION(FLAGS(FLAG_EXIT)),
OPTION_FLAG('r', "recursive", HELP("Recursively remove directories")),
POSITIONAL_STRING("file", HELP("File to remove"))
)
// Define main options with subcommands
CARGS_OPTIONS(
options,
HELP_OPTION(FLAGS(FLAG_EXIT)),
VERSION_OPTION(FLAGS(FLAG_EXIT)),
// Global option applicable to all subcommands
OPTION_FLAG('v', "verbose", HELP("Enable verbose output")),
// Define subcommands
SUBCOMMAND("add", add_options,
HELP("Add files to the index"),
ACTION(add_command)),
SUBCOMMAND("rm", remove_options,
HELP("Remove files from the index"),
ACTION(remove_command))
)
// Implementation of the "add" command
int add_command(cargs_t *cargs, void *data)
{
(void)data; // Unused parameter
// Get the global option
bool verbose = cargs_get(*cargs, ".verbose").as_bool;
// Get command-specific options
const char* file = cargs_get(*cargs, "file").as_string;
bool force = cargs_get(*cargs, "force").as_bool;
printf("Adding file: %s\n", file);
if (verbose) printf(" verbose mode enabled\n");
if (force) printf(" with force option\n");
return 0;
}
// Implementation of the "remove" command
int remove_command(cargs_t *cargs, void *data)
{
(void)data; // Unused parameter
// Get the global option
bool verbose = cargs_get(*cargs, ".verbose").as_bool;
// Get command-specific options
const char* file = cargs_get(*cargs, "file").as_string;
bool recursive = cargs_get(*cargs, "recursive").as_bool;
printf("Removing file: %s\n", file);
if (verbose) printf(" verbose mode enabled\n");
if (recursive) printf(" recursively\n");
return 0;
}
int main(int argc, char **argv)
{
cargs_t cargs = cargs_init(options, "subcommands_example", "1.0.0");
cargs.description = "Example of subcommands";
int status = cargs_parse(&cargs, argc, argv);
if (status != CARGS_SUCCESS) {
return status;
}
if (cargs_has_command(cargs)) {
// Execute the subcommand handler
status = cargs_exec(&cargs, NULL);
} else {
printf("No command specified. Use --help to see available commands.\n");
}
cargs_free(&cargs);
return 0;
}
Next Steps
For more advanced subcommand features, such as:
- Nested subcommands (commands within commands)
- Command name abbreviations
- Alternative positional argument placements
- Custom command handling
See the Nested Commands guide.