Skip to main content

Custom Handlers

Transform raw command-line input into structured data with custom parsing logic for specialized data types.

Handler Function Signature

All custom handlers must follow this signature:

int handler_function(argus_t *argus, argus_option_t *option, char *arg);

Parameters:

  • argus - Context for error reporting
  • option - Where to store the parsed value
  • arg - Raw string to parse (NULL for flags)

Return values:

  • ARGUS_SUCCESS (0) - Parsing succeeded
  • Error code - Parsing failed (use ARGUS_PARSING_ERROR and return error code)

Basic Custom Handler

Parse a "host:port" endpoint into a structured type:

typedef struct {
char *host;
int port;
} endpoint_t;

int endpoint_handler(argus_t *argus, argus_option_t *option, char *arg)
{
// Allocate structure
endpoint_t *endpoint = calloc(1, sizeof(endpoint_t));
if (!endpoint) {
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
return ARGUS_ERROR_MEMORY;
}

// Store immediately for automatic cleanup
option->value.as_ptr = endpoint;
option->is_allocated = true;

// Find separator
char *colon = strchr(arg, ':');
if (!colon) {
ARGUS_PARSING_ERROR(argus, "Invalid format '%s' (expected host:port)", arg);
return ARGUS_ERROR_INVALID_FORMAT;
}

// Extract host
size_t host_len = colon - arg;
endpoint->host = strndup(arg, host_len);
if (!endpoint->host) {
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
return ARGUS_ERROR_MEMORY;
}

// Extract and validate port
long port = strtol(colon + 1, NULL, 10);
if (port <= 0 || port > 65535) {
ARGUS_PARSING_ERROR(argus, "Invalid port %ld (must be 1-65535)", port);
return ARGUS_ERROR_INVALID_VALUE;
}

endpoint->port = (int)port;
return ARGUS_SUCCESS;
}

Helper Macros

Create reusable macros for common custom types:

// Helper macro for endpoint options
#define OPTION_ENDPOINT(short_name, long_name, ...) \
OPTION_BASE(short_name, long_name, VALUE_TYPE_CUSTOM, \
HANDLER(endpoint_handler), \
FREE_HANDLER(endpoint_free_handler), \
##__VA_ARGS__)

// Positional version
#define POSITIONAL_ENDPOINT(name, ...) \
POSITIONAL_BASE(name, VALUE_TYPE_CUSTOM, \
HANDLER(endpoint_handler), \
FREE_HANDLER(endpoint_free_handler), \
##__VA_ARGS__)

Advanced Examples

typedef struct {
char *protocol;
char *host;
int port;
char *path;
char *query;
} url_t;

int url_handler(argus_t *argus, argus_option_t *option, char *arg)
{
url_t *url = calloc(1, sizeof(url_t));
if (!url) {
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
return ARGUS_ERROR_MEMORY;
}

option->value.as_ptr = url;
option->is_allocated = true;

// Parse protocol
char *protocol_end = strstr(arg, "://");
if (protocol_end) {
size_t protocol_len = protocol_end - arg;
url->protocol = strndup(arg, protocol_len);
arg = protocol_end + 3;
} else {
url->protocol = strdup("http");
}

// Parse host and port
char *host_end = strchr(arg, '/');
char *port_start = strchr(arg, ':');

size_t host_len;
if (port_start && (!host_end || port_start < host_end)) {
host_len = port_start - arg;
url->port = atoi(port_start + 1);
} else {
host_len = host_end ? (size_t)(host_end - arg) : strlen(arg);
url->port = (strcmp(url->protocol, "https") == 0) ? 443 : 80;
}

url->host = strndup(arg, host_len);

// Parse path and query
if (host_end) {
char *query_start = strchr(host_end, '?');
if (query_start) {
url->path = strndup(host_end, query_start - host_end);
url->query = strdup(query_start + 1);
} else {
url->path = strdup(host_end);
}
} else {
url->path = strdup("/");
}

return ARGUS_SUCCESS;
}

int url_free_handler(argus_option_t *option)
{
url_t *url = (url_t*)option->value.as_ptr;
if (url) {
free(url->protocol);
free(url->host);
free(url->path);
free(url->query);
free(url);
}
return ARGUS_SUCCESS;
}

Integration with Validation

Combine custom handlers with validators:

// Custom pre-validator for URL format
int url_format_validator(argus_t *argus, void *value_ptr, validator_data_t data)
{
const char *url = (const char *)value_ptr;

// Basic format check before parsing
if (!strstr(url, "://") && !strchr(url, '/')) {
ARGUS_PARSING_ERROR(argus, "URL must contain protocol or path");
return ARGUS_ERROR_INVALID_FORMAT;
}

return ARGUS_SUCCESS;
}

#define V_URL_FORMAT() \
MAKE_VALIDATOR(url_format_validator, _V_DATA_CUSTOM_(NULL), ORDER_PRE)

ARGUS_OPTIONS(
options,
// Handler with pre-validation
OPTION_BASE('u', "url", VALUE_TYPE_CUSTOM,
HANDLER(url_handler),
FREE_HANDLER(url_free_handler),
VALIDATOR(V_URL_FORMAT()),
HELP("Target URL")),
)

Error Handling Best Practices

int safe_handler(argus_t *argus, argus_option_t *option, char *arg)
{
// 1. Allocate main structure
my_type_t *obj = calloc(1, sizeof(my_type_t));
if (!obj) {
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
return ARGUS_ERROR_MEMORY;
}

// 2. Store immediately for automatic cleanup
option->value.as_ptr = obj;
option->is_allocated = true;

// 3. Parse and validate - can exit immediately on error
if (parse_step_1(obj, arg) != 0) {
ARGUS_PARSING_ERROR(argus, "Invalid format");
return ARGUS_ERROR_INVALID_FORMAT;
}

if (parse_step_2(obj, arg) != 0) {
ARGUS_PARSING_ERROR(argus, "Invalid value");
return ARGUS_ERROR_INVALID_VALUE;
}

return ARGUS_SUCCESS;
}

Key points:

  • Validate input early
  • Set option->value.as_ptr and option->is_allocated = true early
  • Use specific error codes
  • ARGUS_PARSING_ERROR reports the error, then return the error code

Memory Management

// Pattern 1: Single allocation
int simple_handler(argus_t *argus, argus_option_t *option, char *arg)
{
my_type_t *obj = malloc(sizeof(my_type_t));
if (!obj) {
ARGUS_PARSING_ERROR(argus, "Allocation failed");
return ARGUS_ERROR_MEMORY;
}

// Store immediately for automatic cleanup
option->value.as_ptr = obj;
option->is_allocated = true;

// Parse into obj...

return ARGUS_SUCCESS;
}

// Pattern 2: Multiple allocations
int complex_handler(argus_t *argus, argus_option_t *option, char *arg)
{
complex_type_t *obj = calloc(1, sizeof(complex_type_t));
if (!obj) {
ARGUS_PARSING_ERROR(argus, "Allocation failed");
return ARGUS_ERROR_MEMORY;
}

// Store immediately for automatic cleanup
option->value.as_ptr = obj;
option->is_allocated = true;

// Additional allocations
obj->data = malloc(data_size);
if (!obj->data) {
ARGUS_PARSING_ERROR(argus, "Data allocation failed");
return ARGUS_ERROR_MEMORY;
}

obj->name = strdup(parsed_name);
if (!obj->name) {
ARGUS_PARSING_ERROR(argus, "Name allocation failed");
return ARGUS_ERROR_MEMORY;
}

return ARGUS_SUCCESS;
}

What's Next?

Handler Design

Design handlers around user-friendly input formats, not internal data structures. Transform complex formats into simple, usable data.