226 lines
10 KiB
C
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
|