Skip to main content
Argus

Modern CLI Parsing for C

Modern C library for command-line argument parsing with a powerful, declarative API

// Traditional approach - manual everything

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
int opt, verbose = 0, port = 8080;
char *output = "output.txt";
char *input = NULL;

struct option long_options[] = {
{"verbose", no_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{"port", required_argument, 0, 'p'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};

while ((opt = getopt_long(argc, argv, "vo:p:h", long_options, NULL)) != -1) {
switch (opt) {
case 'v': verbose = 1; break;
case 'o': output = optarg; break;
case 'p':
port = atoi(optarg);
if (port < 1 || port > 65535) {
fprintf(stderr, "Error: Port must be 1-65535\n");
return 1;
}
break;
case 'h':
printf("Usage: %s [-v] [-o FILE] [-p PORT] INPUT\n", argv[0]);
printf(" -v, --verbose Enable verbose output\n");
printf(" -o, --output Output file (default: output.txt)\n");
printf(" -p, --port Port number (1-65535)\n");
printf(" -h, --help Show this help\n");
return 0;
case '?':
fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
return 1;
}
}

if (optind >= argc) {
fprintf(stderr, "Error: Missing required INPUT file\n");
return 1;
}
input = argv[optind];

// Finally ready to use the parsed arguments...
}

// Argus way - declare once, get everything

#include <argus.h>

ARGUS_OPTIONS(
options,
HELP_OPTION(),
VERSION_OPTION(),
OPTION_FLAG(
'v', "verbose",
HELP("Enable verbose output")
),
OPTION_STRING(
'o', "output",
HELP("Output file"),
DEFAULT("output.txt")
),
OPTION_INT(
'p', "port",
HELP("Port number"),
DEFAULT(8080),
VALIDATOR(V_RANGE(1, 65535))
),
POSITIONAL_STRING(
"input",
HELP("Input file")
),
)

int main(int argc, char **argv)
{
argus_t argus = argus_init(options, "my_tool", "1.0.0");

if (argus_parse(&argus, argc, argv) != ARGUS_SUCCESS)
return 1;

// Type-safe access - validation and help generation automatic
int port = argus_get(&argus, "port").as_int;
bool verbose = argus_get(&argus, "verbose").as_bool;
const char *output = argus_get(&argus, "output").as_string;
const char *input = argus_get(&argus, "input").as_string;

argus_free(&argus);
return 0;
}

> The Right Tool for the Job

Every library has its sweet spot. Here's where each one shines:

⚡ getopt - Maximum Control

Perfect when: Every byte of performance counts, embedded systems, strict memory constraints

You'll need: Manual validation, custom help text, extensive error handling code

🐧 argp - GNU Standard

Perfect when: Building core GNU/Linux utilities, maximum compatibility with existing tools

Limitation: Tied to GNU ecosystem, complex callback patterns for advanced features

📋 argtable3 - Type Safety First

Perfect when: Legacy C compiler constraints, need strong typing without modern features

Missing: No subcommands, manual environment handling, verbose initialization

Our Pick

🚀 Argus - Developer First

Perfect when: You want to focus on your application logic, not argument parsing infrastructure

Modern requirement: C11+ compiler needed, but you get everything built-in

>_

Ready to write less boilerplate?

Join the developers who chose declarative over imperative.
Your future self will thank you.

Easy setup
vcpkg, meson-wrap, xmake-repo
0
dependencies*
All platforms
Linux, macOS, Windows

* PCRE2 optional for regex validation