tildefriends/deps/xopt/xopt.h

226 lines
10 KiB
C

/**
* XOpt - command line parsing library
*
* Copyright (c) 2015 Josh Junon.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef XOPT_H__
#define XOPT_H__
#pragma once
#include <stdio.h>
#include <stdbool.h>
struct xoptOption;
#ifndef offsetof
# define offsetof(T, member) (size_t)(&(((T*)0)->member))
#endif
/**
* Callback type for handling values.
* Called when a command line argument has been
* processed.
*/
typedef void (*xoptCallback)(
const char *value, /* string cmd line option */
void *data, /* custom data structure */
const struct xoptOption *option, /* detected option */
bool longArg, /* true if the long-arg version
was used */
const char **err); /* err output */
enum xoptOptionFlag {
XOPT_TYPE_STRING = 0x1, /* const char* type */
XOPT_TYPE_INT = 0x2, /* int type */
XOPT_TYPE_LONG = 0x4, /* long type */
XOPT_TYPE_FLOAT = 0x8, /* float type */
XOPT_TYPE_DOUBLE = 0x10, /* double type */
XOPT_TYPE_BOOL = 0x20, /* boolean (int) type */
XOPT_PARAM_OPTIONAL = 0x40, /* whether the argument value is
optional */
XOPT_REQUIRED = 0x80 /* indicates the flag must be
present on the command line */
};
enum xoptContextFlag {
XOPT_CTX_KEEPFIRST = 0x1, /* don't ignore argv[0] */
XOPT_CTX_POSIXMEHARDER = 0x2, /* options cannot come after
extra arguments */
XOPT_CTX_NOCONDENSE = 0x4, /* don't allow short args to be
condensed (i.e. `ls -laF') */
XOPT_CTX_SLOPPYSHORTS = 0x8, /* allow short arg values to be
directly after the character */
XOPT_CTX_STRICT = 0x10 /* fails on invalid arguments */
};
typedef struct xoptOption {
const char *longArg; /* --long-arg-name, or 0 for short
arg only */
const char shortArg; /* -s hort arg character, or '\0'
for long arg only */
size_t offset; /* offsetof(type, property) for
automatic configuration handler */
xoptCallback callback; /* callback for resolved option
handling */
long options; /* xoptOptionFlag options */
const char *argDescrip; /* --argument=argDescrip (autohelp) */
const char *descrip; /* argument explanation (autohelp) */
} xoptOption;
/* option list terminator */
#define XOPT_NULLOPTION {0, 0, 0, 0, 0, 0, 0}
typedef struct xoptContext xoptContext;
typedef struct xoptAutohelpOptions {
const char *usage; /* usage string, or null */
const char *prefix; /* printed before options, or null */
const char *suffix; /* printed after options, or null */
size_t spacer; /* number of spaces between option and
description */
} xoptAutohelpOptions;
#ifdef __cplusplus
extern "C" {
#endif
/**
* Creates an XOpt context to be used with
* subsequent calls to XOpt functions
*/
xoptContext*
xopt_context(
const char *name, /* name of the argument set (usually
name of the cli binary file/cmd */
const xoptOption *options, /* list of xoptOption objects,
terminated with XOPT_NULLOPTION */
long flags, /* xoptContextFlag flags */
const char **err); /* pointer to a const char* that
receives an err should one occur -
set to 0 if command completed
successfully */
/**
* Parses the command line of a program
* and returns the number of non-options
* returned to the `extras' pointer (see
* below)
*/
int
xopt_parse(
xoptContext *ctx, /* previously created XOpt context */
int argc, /* argc, from int main() */
const char **argv, /* argv, from int main() */
void *data, /* a custom data object whos type
corresponds to `.offset' values
specified in the options list;
populated with values interpreted
from the command line */
const char ***extras, /* receives a list of extra non-option
arguments (i.e. files, subcommands,
etc.) - length of which is returned
by the function call */
const char **err); /* pointer to a const char* that
receives an err should one occur -
set to 0 if command completed
successfully */
/**
* Generates and prints a help message
* and prints it to a FILE stream.
* If `defaults' is supplied, uses
* offsets (values) defined by the options
* list to show default options
*/
void
xopt_autohelp(
xoptContext *ctx, /* previously created XOpt context */
FILE *stream, /* a stream to print to - if 0,
defaults to `stderr'. */
const xoptAutohelpOptions *options, /* configuration options to tailor
autohelp output */
const char **err); /* pointer to a const char* that
receives an err should one occur -
set to 0 if command completed
successfully */
/**
* Generates a default option parser that's sane for most cases.
*
* Assumes there's a `help` property that is boolean-checkable that exists on the
* config pointer passed to `config_ptr` (i.e. does a lookup of `config_ptr->help`).
*
* In the event help is invoked, xopt will `goto xopt_help`. It is up to you to define such
* a label in order to recover. In this case, extrav will still be allocated and will still need to be
* freed.
*
* To be extra clear, you need to free `extrav_ptr` is if `*err_ptr` is not `NULL`.
*
* `name` is the name of the binary you'd like to pass to the context (welcome to use `argv[0]` here),
* `options` is a reference to the xoptOptions array you've specified,
* `config_ptr` is a *pointer* to your configuration instance,
* `argc` and `argv` are the int/const char ** passed into main,
* `extrac_ptr` and `extrav_ptr` are pointers to an `int`/`const char **`
* (so `int*` and `const char ***`, respectively) that receive the parsed extra args
* (note that, unless there is an error, `extrav_ptr` is owned by your program and must
* be `free()`'d when you're done using it, even if there are zero extra arguments),
* and `err_ptr` is a pointer to a `const char *` (so a `const char **`) that receives any error
* strings in the event of a problem. These errors are statically allocated so no need to
* free them. This variable should be initialized to NULL and checked after calling
* `XOPT_SIMPLE_PARSE()`.
*
* `autohelp_file`, `autohelp_usage`, `autohelp_prefix`, `autohelp_suffix` and `autohelp_spacer` are all
* parameters to the `xoptAutohelpOptions` struct (with the exception of `autohelp_file`, which must be a
* `FILE*` reference (e.g. `stdout` or `stderr`) which receives the rendered autohelp text). Consult the
* `xoptAutohelpOptions` struct above for documentation as to valid values for each of these properties.
*/
#define XOPT_SIMPLE_PARSE(name, flags, options, config_ptr, argc, argv, extrac_ptr, extrav_ptr, err_ptr, autohelp_file, autohelp_usage, autohelp_prefix, autohelp_suffix, autohelp_spacer) do { \
xoptContext *_xopt_ctx; \
*(err_ptr) = NULL; \
_xopt_ctx = xopt_context((name), (options), ((flags) ^ XOPT_CTX_POSIXMEHARDER ^ XOPT_CTX_STRICT), (err_ptr)); \
if (*(err_ptr)) break; \
*extrac_ptr = xopt_parse(_xopt_ctx, (argc), (argv), (config_ptr), (extrav_ptr), (err_ptr)); \
if ((config_ptr)->help) { \
xoptAutohelpOptions __xopt_autohelp_opts; \
__xopt_autohelp_opts.usage = (autohelp_usage); \
__xopt_autohelp_opts.prefix = (autohelp_prefix); \
__xopt_autohelp_opts.suffix = (autohelp_suffix); \
__xopt_autohelp_opts.spacer = (autohelp_spacer); \
xopt_autohelp(_xopt_ctx, (autohelp_file), &__xopt_autohelp_opts, (err_ptr)); \
if (*(err_ptr)) goto __xopt_end_free_extrav; \
goto xopt_help; \
} \
if (*(err_ptr)) goto __xopt_end_free_ctx; \
__xopt_end_free_ctx: \
free(_xopt_ctx); \
break; \
__xopt_end_free_extrav: \
free(*(extrav_ptr)); \
free(_xopt_ctx); \
break; \
} while (false)
#ifdef __cplusplus
}
#endif
#endif